I have a method which writes a XML- File like this:
private void doProcess() {
Element rootElement = mDoc.createElement("Test");
mDoc.appendChild(rootElement);
....... I build the whole document here...
}
But this method can be called by multiple Threads, so for example if thwo threads call this method on the same time i get an
): org.w3c.dom.DOMException: Only one root element allowed
I already tried it with an reentrantlock, but this didn´t work...Can somebody give me a hint?
Edit:
I dont build the Document with multiple Threads...Every Call of my Method builds his own Doc...So sometimes in my Application it could happen that my Method will be called two times at the same time...And there´s my Problem...
In the question you state:
I dont build the Document with multiple Threads...Every Call of my Method builds his own Doc
Currently the code given shares a single doc between all calls to the function. In order to have each call to the function work on it's own document, you need to modify the code such that each call has it's own doc.
This can be done either by creating and returning a new document object
private XMLDocument doProcess() {
XMLDocument mDoc = new XMLDocument(); // or simmilar depending on XML library
Element rootElement = mDoc.createElement("Test");
mDoc.appendChild(rootElement);
// ....... I build the whole document here...
return mDoc; //return the document object
}
Or, by passing the document object in as a parameter
private void doProcess(XMLDocument mDoc) { ... }
An xml can has only ONE root, so this may be answer to your question. You can instantiate an root element outside this method and add element to this root inside method each time.
Related
I have an XPage that is saving a document inside SSJS with document1.save(). After this, I call some Java code to do some additional processing of the document and the new data that was saved; I pass document1.getDocument() in to the Java function. In the Java function, it calls Document.save() to save the document again. This seems to be a recipe for getting a save conflict, and I don't know why. Can anyone explain what's happening? TIA! (In addition to understanding why this is happening, if anyone has suggestions for a better way to do what I'm doing, I'd appreciate it.)
Reid
You can use "resolveVariable" in Java to get hold of your NotesXspDocument (which is called DominoDocument in Java). You can then do your save on the DominoDocument object in Java instead of in SSJS.
If you use JSFUtil (which is found in many XPages open source projects) or use your own helper method, you can then do this to get hold of your DominoDocument (replace "currentDocument" with the name of your document data source):
DominoDocument uidoc = (DominoDocument) JSFUtil.resolveVariable("currentDocument");
The resolveVariable method looks like this:
public static Object resolveVariable(final String variable) {
return FacesContext.getCurrentInstance().getApplication().getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(), variable);
}
I've been trying to create an XML document in Java using DOM where there are multiple car elements of the same name on the same hierarchical level, similar to the following:
<Root>
<Car>
<Make>Ford</Make>
<Model>Fiesta</Model>
</Car>
<Car>
<Make>VW</Make>
<Model>Golf</Model>
</Car>
</Root>
However, during construction of the XML, whenever I attempt to add another Car element, it seems to override the one that is already there, resulting in me only getting one Car element in my output.
I create the car element using the following code:
Element carElement = doc.createElement("Car");
And then attempt to append to the first Car element that I create using the following code:
root.appendChild(carElement);
I have also tried the following code to no avail:
Node existingCar = doc.getElementsByTagName("Car").item(0);
existingCar.getParentNode().insertBefore(carElement, existingCar);
The Java docs state that for both the appendChild() and insertBefore() methods, if the newChild node exists, then it will firstly be removed - hence why I think Im only seeing one output in my XML.
Therefore, can someone confirm whether or not this is possible with DOM? If so, can they please advise or point me in the direction of a solution? Thanks
I can confirm that this is possible with DOM!
You haven't shown us your actual code where you try to add more than one child element with the same name, so we cannot tell you exactly why it doesn't work. However, maybe this snippet will give you a hint of how to fix your code. To add five Carelements:
DocumentBuilder b = ...;
DOMImplementation impl = b.getDOMImplementation();
Document d = impl.createDocument(null, "Root", (DocumentType) null);
Element root = d.getDocumentElement();
for (int i = 0; i < 5; ++i) {
Element car = d.createElement("Car");
// add sub-elements/attributes to car element
...
root.appendChild(car);
}
I have this code:
Public static List <LinkNode> parse (LinkNode inputLink) {
List <LinkNode> outputLinks = new LinkList<>();
try {
Document parsedResults = Jsoup
.connect (inputLink.getUrl ())
.timeout (READ_TIMEOUT_IN_MILLISSECS)
.get ();
}
String tag;
Elements elements;
List <LinkNode> result;
}
I have few questions about the code:
Is Document a reserved keyword Java?
try {
Document parsedResults = Jsoup
.connect (inputLink.getUrl ())
.timeout (READ_TIMEOUT_IN_MILLISSECS)
.get ();
What is this form of coding (dot something)? Can I write it in another form If yes can you show me how?
.connect (inputLink.getUrl ())
.timeout (READ_TIMEOUT_IN_MILLISSECS)
.get ();
No, it is not a reserved keyword. In this case, Document is the name of a class defined in the Jsoup library. Other libraries can have their own class named Document, or you could write your own. For example:
In contrast, reserved keywords cannot be used as identifiers (i.e. class names, function names, variable names). These words include class, try, if, while, public, private, and more. See a bigger list here.
The dot (.) operator allows you to access an object's functions or variables. For example rectangle.width or dog.run().
Your example shows method chaining, in which subsequent methods are called on the object returned by the previous method. This removes the need for creating intermediate variables and makes things easier (for you as a programmer and also someone that is reading your code).
You could achieve exactly the same result with more code:
Connection conn1 = Jsoup.connect(inputLink.getUrl ());
Connection conn2 = conn1.timeout(READ_TIMEOUT_IN_MILLISSECS);
Document parsedResults = conn2.get();
1) No, it's a class defined in the Jsoup library.
2) It's part of a normal method call, as in Integer.parseInt("5") or scanner.readLine().
Say I have an object that I've created to further simplify reading an XML document using the DOM parser. In order to "step into" a node or element, I'd like to use a single line to go from the start of the document to my target data, buried somewhere within the document, while bypassing the extra "fluff" of the DOM parser (such as doc.getElementsByTagName("data").item(0) when there is only one item inside the "data" element).
For the sake of this question, let's just assume there are no duplicate element tags and I know where I need to navigate to to get the data I need from the document, of which the data is a simple string. The idea is to set the simplified reader up so that it can be used for other data in other locations in the document, as well, without having to write new methods all the time. Below is some example code I've tried:
public class SimplifiedReader {
Document doc;
Element ele;
public SimplifiedReader(Document doc) {
this.doc = doc;
ele = doc.getDocumentElement();
}
public SimplifiedReader fromRoot() {
ele = doc.getDocumentElement();
return this;
}
public SimplifiedReader withEle(String elementName) {
ele = ele.getElementsByTagName(elementName).item(0);
return this;
}
public String getTheData(String elementName) {
return ele.getTextContent();
}
}
Example XML File:
<?xml version="1.0" encoding="UTF-8"?>
<fileData>
<subData>
<targetData>Hello World!</targetData>
<otherData>FooBar!</otherData>
</subData>
</fileData>
This results in me being able to navigate the XML file, and retrieve the Strings "Hello World!" and "FooBar!" using this code:
SimplifiedReader sr = new SimplifiedReader(doc);
String hwData = sr.withEle("fileData").withEle("subData").getTheData("targetData");
String fbData = sr.getTheData("otherData");
Or, if I had to go to another thread to get the data "FooBar!", I would just do:
String fbData = sr.fromRoot().withEle("fileData2").withEle("subData2").getTheData("otherData");
Is there a better/more correct way to do this? Edit: Note: This question is more about the method of returning an object from a method inside of it (return this;) in order to reduce the amount of code written to access specific data stored within a tree format and not so much about how to read an XML file. (I originally thought this was the Singleton Pattern until William corrected me... thank you William).
Thanks in advance for any help.
I don't see any trace of the Singleton pattern here. It mostly resembles the Builder pattern, but isn't it, either. It just implements a fluent API.
Your approach seems very nice and practical.
I would perhaps advise not using fromRoot() but instead constructing a new instance each time. The instance is quite lightweight since all the heavyweight stuff resides in the Document instance it wraps.
You could even go immutable all the way, returning a new instance from withEle(). This buys you many cool properties, like the freedom to share the object around, each code path being free to use it as a starting point to fetch something specific relative to it, share it across threads, etc. The underlying Document is mutable, but usually this doesn't create real-life problems when the code is all about reading.
Is there a better/more correct way to do this?
Yes, there are many better ways to extract values from XML.
One would be to use XPath, for example with XMLBeam.
import java.io.IOException;
import org.xmlbeam.XBProjector;
import org.xmlbeam.annotation.XBDocURL;
import org.xmlbeam.annotation.XBRead;
public class App {
public static void main(String[] args) throws IOException {
FileDate fileDate = new XBProjector().io().fromURLAnnotation(FileDate.class);
System.out.println(fileDate.getTargetDate());
// Hello World!
System.out.println(fileDate.getOtherDate());
// FooBar!
}
#XBDocURL("resource://filedate.xml")
public interface FileDate {
#XBRead("/fileData/subData/targetData")
String getTargetDate();
#XBRead("/fileData/subData/otherData")
String getOtherDate();
}
}
I want to parse a large xml file using dom4j. I'm using the dom4j's feature that you can register event handlers for path expressions for ignoring the elements I don't care about. The feature is explained here: http://dom4j.sourceforge.net/dom4j-1.6.1/faq.html#large-doc.
I quote from there:
"These handlers will then be called on the start and end of each path registered against a particular handler. When the start tag of a path is found, the onStart method of the handler registered to the path is called. When the end tag of a path if found, the onEnd method of the handler registered to that path is called.
The onStart and onEnd methods are passed an instance of an ElementPath, which can be used to retrieve the current Element for the given path. If the handler wishes to "prune" the tree being built in order to save memory use, it can simply call the detach() method of the current Element being processed in the handlers onEnd() method."
My problem is that I don't know what path should I give so that all the children of the root node to be handled by the 2 methods.
My xml file is something like:
<root .....>
<chef name="" ..../>
<chef name="" ..../>
<recipe name = .... />
<recipe name...../>
....
If I would like to handle chef elements than the path would be /root/chef.
For recipe elements the path would be /root/recipe.
But what is the path that should be given to the dom4j so that it will handle (in the onStart(), onEnd()) both chef and recipe elements?
Thanks a lot!
Instead of calling the addHandler() method, call the setDefaultHandler() and use it like this:
SAXReader reader = new SAXReader();
reader.setDefaultHandler(
new ElementHandler() {
public void onStart(ElementPath path) {
// If needed, similar to onEnd, but don't detach.
}
public void onEnd(ElementPath path) {
Element parent = path.getCurrent().getParent();
if(parent != null && "/root".equals(parent.getPath()) {
// Do whatever
}
path.getCurrent().detach();
}
}
);
Try //root/child::* or //root/descendant::* depending on what level of depth you want.
see w3schools for more on the available xpath axes