Adding sub-element to element java (DOM) - java

Essentially, i'm creating an XML document from a file (a database), and then i'm comparing another parsed XML file (with updated information) to the original database, then writing the new information into the database.
I'm using java's org.w3c.dom.
After lots of struggling, i decided to just create a new Document object and will write from there from the oldDocument and newDocument ones i'm comparing the elements in.
The XML doc is in the following format:
<Log>
<File name="something.c">
<Warning file="something.c" line="101" column="23"/>
<Warning file="something.c" line="505" column="71" />
</File>
</Log>
as an example.
How would i go about adding in a new "warning" Element to the "File" without getting the pesky "org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it." exception?
Cutting it down, I have something similar to:
public static Document update(Element databaseRoot, Element newRoot){
Document doc = db.newDocument(); // DocumentBuilder defined previously
Element baseRoot = doc.createElement("Log");
//for each file i have:
Element newFileRoot = doc.createElement("File");
//some for loop that parses through each 'file' and looks at the warnings
//when i come to a new warning to add to the Document:
NodeList newWarnings = newFileToCompare.getChildNodes(); //newFileToCompare comes from the newRoot element
for (int m = 0; m < newWarnings.getLength(); m++){
if(newWarnings.item(m).getNodeType() == Node.ELEMENT_NODE){
Element newWarning = (Element)newWarnings.item(m);
Element newWarningRoot = (Element)newWarning.cloneNode(false);
newFileRoot.appendChild(doc.importNode(newWarningRoot,true)); // this is what crashes
}
}
// for new files i have this which works:
newFileRoot = (Element)newFiles.item(i).cloneNode(true);
baseRoot.appendChild(doc.importNode(newFileRoot,true));
doc.appendChild(baseRoot);
return doc;
}
Any ideas? I'm beating my head against the wall. First time doing this.

Going through with a debugger I verified that the document owners were correct. Using node.getOwnerDocument(), I realized that the newFileRoot was connected to the wrong document earlier when I created it, so I changed
Element newFileRoot = (Element)pastFileToFind.cloneNode(false);
to
Element newFileRoot = (Element)doc.importNode(pastFileToFind.cloneNode(false),true);
since later on when i was trying to add the newWarningRoot to newFileRoot, they had different Documents (newWarningRoot was correct but newFileRoot was connected to the wrong document)

Related

How to add new attribute into XML string in JAVA? Condition: based on parent Key and I can use only JAVA internal api if we need to do parse or SAX

I have a following xml string.
<aa>
<bb>
<cc>
<cmd>
<efg sid="C1D7B70D7AF705731B0" mid="C1D7D7AF705731B0" stid="-1" dopt="3">
<pqr>
<dru fo="1" fps="1" nku="WBECDD6CC37656E6C9" tt="1"/>
<dpo drpr="67" dpi="16"/>
<dres >
<dre dreid="BB:8D679D3511D3E4981000E787EC6DE8A4:1:1:0:2:1" fa="1" dpt= "1" o="0"/>
</dres>
</pqr>
</efg>
</cmd>
</cc>
</bb>
</aa>
I need to add "login" attribute inside <efg> tag. So new XML would be
<aa>
<bb>
<cc>
<cmd>
<efg sid="C1D7B70D7AF705731B0" login="sdf34234dfs" mid="C1D7D7AF705731B0" stid="-1" dopt="3">
<pqr>
<dru fo="1" fps="1" nku="WBECDD6CC37656E6C9" tt="1"/>
<dpo drpr="67" dpi="16"/>
<dres >
<dre dreid="BB:8D679D3511D3E4981000E787EC6DE8A4:1:1:0:2:1" fa="1" dpt= "1" o="0"/>
</dres>
</pqr>
</efg>
</cmd>
</cc>
</bb>
</aa>
Condition is:
I can only use inbuilt Java API (java 8) or SAX parser or xmlbuilder
Add condition is based on Parent tag i.e need to check <cmd> then in child need to add <login> because it is not sure always that <efg> tag would always be there with the same name, it could be with any name.
I have tried with DOM parser with following code.
String xml = "xmlString";
//Use method to convert XML string content to XML Document object
Document doc = convertStringToXML( xml );
doc.getDocumentElement().normalize();
Node m = doc.getElementsByTagName("cmd").item(0).getFirstChild();
Attr login = doc.createAttribute("login");
login.setValue("123567");
m.appendChild(login);
However, I am getting following error in my last line of code.
Exception in thread "main" org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted.
Please anyone suggest me, how to add new attribute login into based on my condition no 2.
NodeList nodeList = doc.getElementsByTagName("cmd");
//Check <cmd> tag is present and then check <cmd> tag has child nodes
if (nodeList != null && nodeList.item(0).hasChildNodes()) {
//Get first child node of <cmd> xml tag
String nodeName = doc.getElementsByTagName("cmd").item(0).getFirstChild().getNodeName();
NodeList childNodeList = doc.getElementsByTagName(nodeName);
Element el = (Element) childNodeList.item(0);
//set pgd_login attribute with respective value
el.setAttribute("login", "xyz");
//Convert back into xml string from Document
xml = XMLHelpers.TransformDOMDocumentToString(doc);
}

CreateTextNode escape characters in large text string

charactersI am trying to include the correct characters in an XML document text node:
Element request = doc.createElement("requestnode");
request.appendChild(doc.createTextNode(xml));
rootElement.appendChild(request);
The xml string is a segment of a large xml file which I have read in:
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement("rootnode");
doc.appendChild(rootElement);
<firstname>John</firstname>
<dateOfBirth>28091999</dateOfBirth>
<surname>Doe</surname>
The problem is that passing this into createTextNode is replacing some of the charters:
<firstname>John</firstname>
<dateOfBirth>28091999</dateOfBirth>
<surname>Doe</surname>
Is there any way I can keep the correct characters (< , >) in the textnode. I have read about using importnode but this is not correctly XML, only a segment of a file.
Any help would be greatly appreciated.
EDIT: I need the xml string (which is not fully formatted xml, only a segment of an external xml file) to be in the "request node" as I am building XML to be imported into SOAP UI
You can't pass the element tag and text to the createTextNode() method. You only need to pass the text. You need then to append this text node to an element.
If the source is another XML document, you must extract the text node from an element and insert it in to the other. You can grab a Node (element and text) and try to inserted as a text node in the other. That is why you are seeing all the escape characters.
On the other hand, you can insert this Node into the other XML (if the structure is allowed) and it should be just fine.
In your context, I assume "request" is some sort of Node. The child element of a Node could be another element, text, etc. You have to be very specific.
You can do something like:
Element name = doc.createElement("name");
Element dob = doc.createElement("dateOfBirth");
Element surname = doc.createElement("surname");
name.appendChild( doc.createTextNode("John") );
dob.appendChild( doc.createTextNode("28091999") );
surname.appendChild( doc.createTextNode("Doe") );
Then you can add these element to a parent node:
node.appendChild(name);
node.appendChild(dob);
node.appendChild(surname);
UPDATE: As an alternative, you can open a stream to a document and insert your XML string as a byte stream. Something like this (untested code, but close):
String xmlString = "<firstname>John</firstname><dateOfBirth>28091999</dateOfBirth><surname>Doe</surname>";
DocumentBuilderFactory fac = javax.xml.parsers.DocumentBuilderFactory.newInstance();
DocumentBuilder builder = fac.newDocumentBuilder();
Document newDoc = builder.parse(new ByteArrayInputStream(xmlString.getBytes()));
Element newElem = doc.createElement("whatever");
doc.appendChild(newElem);
Node node = doc.importNode(newDoc.getDocumentElement(), true);
newElem.appendChild(node);
Something like that should do the trick.

Reading XML in java using DOM

I am new to read XML in Java using DOM. Could someone help me with simple code steps to read this XML in DOM?
Here is my XML:
<DataSet xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:noNamespaceSchemaLocation='datamartschema.1.3.xsd'>
<DataStream title='QUESTIONNAIRE'>
<Record>
<TransactionDate>2014-05-28T14:17:31.2186777-06:00</TransactionDate><SubType>xhaksdj</SubType>
<IntegerValue title='ComponentID'>11111</IntegerValue>
</Record><Record>
<TransactionDate>2014-05-28T14:17:31.2186777-06:00</TransactionDate><SubType>jhgjhg</SubType>
<IntegerValue title='ComponentID'>11111</IntegerValue>
</Record>
</DataStream>
</DataSet>
In this XML I need to read the DataStream value and Record values. My expected output is
DataStream=QUESTIONNAIRE and my records are
<TransactionDate>2014-05-28T14:17:31.2186777-06:00</TransactionDate><SubType>xhaksdj</SubType><IntegerValue title='ComponentID'>11111</IntegerValue><TransactionDate>2014-05-28T14:17:31.2186777-06:00</TransactionDate><SubType>jhgjhg</SubType><IntegerValue title='ComponentID'>11111</IntegerValue>
How can I get this output? I tried myself but I can't get the records output like above. I get the output without Tags which are present in the above output.I am using this line to get the output. But it does not give me correct output. Also, how to read the datastream value from this XML? Kindly help me.
This is my code snippets
NodeList datasetallRecords = indElement.getElementsByTagName("Record");
for (int y = 0; y < datasetallRecords.getLength(); y++) {
Element recordsElement = (Element) datasetallRecords.item(y);
recordXMl = recordXMl + recordsElement.getTextContent();
String d = datasetallRecords.item(y).getTextContent();
if (recordsElement.getTagName().equalsIgnoreCase("SubType")) {
lsDataStreamSubTypes.add(recordsElement.getTextContent());
}
recordCount = y;
}
When you create new instance of builder you can get DataStream
it would be look like this:
Element root = document.getDocumentElement();
NodeList dataStreams = root.getElementsByTagName("DataStream");
then get throw this list and get all info like this:
for (int i = 0; i < dataStreams.lenght(); i++) {
Element dataStream = (Element) dataStreams.item(i);
if (dataStream.getNodeType() == Element.ELEMENT_NODE) {
String title = dataStream.getAttributes()
.getNamedItem("title").getTextContent();
}
}
First you need to create a Node like this
Node nNode = datasetallRecords.item(y);
then an element like this
Element eElement = (Element) nNode;
now you can start taking the values from the element by using the getelementbyid and getnodevalue method.
You're not getting the tags because the call to getTextContent() on the "Record" node will return only the textual content of that node and its descendants.
If you need to nodes as well you'll have to process the XML by hand. Have a look at the DOM tutorial it covers processing a document in DOM mode very well including how to read out element names.

Inserting a node in an xml file

I would like to insert a node in an xml file using Java DOM. I am actually editing a lot of contents of a dummy file in order to mofidy it like the original.
I would want to add an open node and close node in between the following file;
<?xml version="1.0" encoding="utf-8"?>
<Memory xmlns:xyz="http://www.w3.org/2001/XMLSchema-instance"
xmlns:abc="http://www.w3.org/2001/XMLSchema" Derivative="ABC"
xmlns="http://..">
///////////<Address> ///////////(which I would like to insert)
<Block ---------
--------
-------
/>
////////// </Address> /////////(which I would like to insert)
<Parameters Thread ="yyyy" />
</Memory>
I hereby request you to let me know how to I insert -- in between the xml file?
Thanks in advance.!
What I have tried doing is;
Element child = doc.createElement("Address");
child.appendChild(doc.createTextNode("Block"));
root.appendChild(child);
But this gives me an output like;
<Address> Block </Address> and not the way i expect :(
And now, what I have tried is to add these lines;
Element cd = doc.createElement("Address");
Node Block = root.getFirstChild().getNextSibling();
cd.appendChild(Block);
root.insertBefore(cd, root.getFirstChild());
But still, this is not the output which i am looking for. I got this output as
---------
What you want is probably:
Node parent = block.getParentNode()
Node blockRemoved = parent.removeChild(block);
// Create address
parent.appendChild(address);
address.appendChild(blockRemoved);
This is the standard way to re-attach a node in another place under W3C DOM.
Here:
DocumentBuilder b = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = b.parse(...);
// Parent of existing Block elements and new Address elemet
// Might be retrieved differently depending on
// actual structure
Element parent = document.getDocumentElement();
Element address = document.createElement("address");
NodeList nl = parent.getElementsByTagName("Block");
for (int i = 0; i < nl.getLength(); ++i) {
Element block = (Element) nl.item(i);
if (i == 0)
parent.insertBefore(address, block);
parent.removeChild(block);
address.appendChild(block);
}
// UPDATE: how to pretty print
LSSerializer serializer =
((DOMImplementationLS)document.getImplementation()).createLSSerializer();
serializer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);
LSOutput output =
((DOMImplementationLS)document.getImplementation()).createLSOutput();
output.setByteStream(System.out);
serializer.write(document, output);
I assume you are using the W3C DOM (e.g. http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html ). If so try
insertBefore(address, block);

Adding a new node before the first child in dom tree

This is probably one of those easiest things to deal with but for some reason not working for me. I'm trying to add a new node after the root in dom tree.
Here's the original string:
<div class="discussionThread dt"><div class="dt_subject">2011 IS HERE!</div></div>
I'm trying to add a new node which is in the form of a string before . The final version should look like:
<div class="discussionThread dt"><div class="test">Test Val</div><div class="dt_subject">2011 IS HERE!</div></div>
As you can see, the new Test Val is being added immediately after the root div class. I've used few methods to place the node at the right place but its getting appended at the end.
Here's a sample which I referred from one of the earlier posts:
String newNode = "<div class="test">test</div>";
SAXReader reader = new SAXReader();
Document newNodeDocument = reader.read(new StringReader(newNode));
Document originalDoc = new SAXReader().read(new StringReader(content));
Element root = originalDoc.getRootElement();
Element givenNode = originalDoc.getRootElement();
givenNode.add(newNodeDocument.getRootElement());
This is resulting the node getting added at the end. I tried using insertBefore(), but didn't work out.
Any pointers will be highly appreciated.
Thanks
Why create a new Document or a new root Element? I think the shortest way is using Branch#content:
Returns the content nodes of this
branch as a backed List so that
the content of this branch may be
modified directly using the interface.
The List is backed by the Branch so
that changes to the list are reflected
in the branch and vice versa.
You just have to create the new Element and to add it to the root element through the List provided by content method (passing it the position index), this is my main:
public static void main(String[] args) throws DocumentException {
SAXReader reader = new SAXReader();
String xml = "<div class=\"discussionThread dt\"><div class=\"dt_subject\">2011 IS HERE!</div></div>";
Document document = reader.read(new StringReader(xml));
DefaultElement newElement = new DefaultElement("div");
newElement.addAttribute("class", "test");
newElement.add(new DefaultText("Test Val"));
List content = document.getRootElement().content();
if (content != null ) {
content.add(0, newElement);
}
System.out.println(document.asXML());
}
which prints out the following xml:
<div class="discussionThread dt"><div class="test">Test Val</div><div class="dt_subject">2011 IS HERE!</div></div>
In addition, you should also consider the use of xslt when you have to transform xml.
You're calling Element#add(Entity). From the Javadocs:
Adds the given Entity to this element. If the given node already has a parent defined then an IllegalAddException will be thrown.
So the new node you're adding will be added as a child of the node you're adding it to. You cannot add another node after the root node you have, because a document can have only one root node.
What you can do is create a new root node, then add the old root node and the new node as children of this new root node. Then set the root node of the document to the new root node.
Why not just create a new Document with the value you want, then append the other nodes to it?

Categories