Insert node as first child always - java

I have a basic XML document setup like this:
<rss>
<channel>
</channel>
</rss>
I would like to add new child nodes of the channel as the first node, so, say I'm looping through a collection of items, then the first item would be added like this:
<rss>
<channel>
<item>Item 1</item>
</channel>
</rss>
Then the next item would added like this:
<rss>
<channel>
<item>Item 2</item>
<item>Item 1</item>
</channel>
</rss>
I've been trying to use:
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(new File(xmlFile));
Element itemNode = doc.createElement("item");
Node channelNode = doc.getElementsByTagName("channel").item(0);
channelNode.appendChild(itemNode);
But it keeps adding new items to the bottom of the list.

channelNode.appendChild(itemNode);
will always append the itemNode to the end of the list of children of channelNode. This is behavior is defined in the DOM Level 3 specification, and in the JAXP documentation as well:
Node appendChild(Node newChild)
throws DOMException
Adds the node newChild to the end of the list of children of this
node (emphasis mine). If the newChild is already in the tree, it is first removed.
If you need to add at the beginning of the list of child nodes, you can use the insertBefore() API method instead:
channelNode.insertBefore(itemNode, channelNode.getFirstChild());

Related

Why are xmlns="" added to element in Java automatically?

I am creating an XML file using Java.
I created root node using createElemenNS() to createh root node with namespace.
Element root = doc.createElementNS("http://www.myorc.com/schemas", "InvConf");
Then I created a node using createElement() and added it to root node. This node is automatically added with namespace like below.
Element invList = doc.createElement("InvList");
root.appendChild(invList);
<InvConf xmlns="http://www.myorc.com/schemas">
<InvList xmlns="">
...
</InvList>
<InvList xmlns="">
...
</InvList>
<InvList xmlns="">
...
</InvList>
</InvConf>
How to avoid adding the namespace to child nodes ?
I want the final XML to be like the following
<InvConf xmlns="http://www.myorc.com/schemas">
<InvList>
...
</InvList>
<InvList>
...
</InvList>
<InvList>
...
</InvList>
</InvConf>
Found that issues is coming only when xmlparserv2.jar is in CLASSPATH. This is required by some parts for the application. How to resolve this ?
The xmlns="" were added because your child is not in a namespace, and your parent is. To change that, put it in the namespace at the time you create the element.
Change the
createElement("InvList");
To the correct namespace.
As pointed out in comments xmlns="" means that element doesn't have any namespace. E.g. from XML parser point of view following two documents are identical:
<ns:root xmlns:ns="http://namespace.com">
<child/>
</ns:root>
and
<root xmlns="http://namespace.com">
<child xmlns=""/>
</root>
To avoid creation of xmlns="" in elements not belonging to any namespace you can create prefix on upper level element:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
Element root = doc.createElementNS("http://namespace.com", "root");
root.setPrefix("ns");
Element child = doc.createElementNS("", "child");
root.appendChild(child);
doc.appendChild(root);
This code will create following XML:
<ns:root xmlns:ns="http://namespace.com">
<child/>
</ns:root>
Alternatively you can use following syntax to achieve same result:
Document doc = db.newDocument();
Element root = doc.createElementNS("http://namespace.com", "ns:root");
Element child = doc.createElementNS("", "child");
root.appendChild(child);
doc.appendChild(root);
When commenting out line root.setPrefix("ns"); or creating element without prefix (doc.createElementNS("http://namespace.com", "root");) following XML will be generated:
<root xmlns="http://namespace.com">
<child xmlns=""/>
</root>

Get XML elements which have required attribute using Java Xml Parser

I have XML file like
<Parent>
<child1 key= "">
<sub children>
</child1>
<child2 key="">
<sub children>
</child2>
</parent>
In this XML file I would like to get all nodes which have attribute 'key'.
How to achieve this using best Java XML Parser?
I tried with StAX parser but it has to check every element to check whether it has attribute 'key' or not. So, it takes time to give output in case of large files.
xpath for nodes with key (empty or not):
expression="//*[#key]";
or, for didactic purpose: empty (#key='') or not empty (string(#key))
expression="//*[(#key='')or(string(#key))]";
To parse with DOM, there are many examples abroad.
standard code:
DocumentBuilderFactory builderFactory =DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(xml)));
XPath xpath = XPathFactory.newInstance().newXPath();
String expression="//*[(#key='')or(string(#key))]";
Set<String> towns=new HashSet<String>();
XPathExpression expr = xpath.compile(expression) ;
NodeList nodes = (NodeList) expr.evaluate(document, XPathConstants.NODESET);

Prevent JDOM2 from creating xmlns=""

I try to add new <class> elements to a persistence.xml file with JDOM2.
persistenceUnitEl.add(new Element("class").addContent(className));
The problem is that jdom2 always adds xmlns="" to the <class> elements.
How can i prevent this?
removeAttribute("xmlns") does not work and removeNameSpace(el.getNameSpace()) also does not work.
JDOM only adds the xmlns="" if you add child elements to other elements that are already in a namespace. The default Namespace in XML is the one which has no prefix. In the following example:
<root>
<child />
</root>
There are no namespace prefixes, and the default namespace is "".
The above XML snippet is semantically identical to:
<root xmlns="" >
<child />
</root>
The xmlns="" means that, any time you see an element that has no prefix, that you should put it in the 'empty' namespace "".
Now, if you want to put things in a namespace, and have a prefix, you would do:
<ns:root xmlns:ns="http://mynamespace">
<ns:child />
</ns:root>
Note that the root and child elements in the above example are in the namespace http://mynamespace, and that namespace has the prefix ns. The above code would be semantically identical to (has the same meaning as):
<root xmlns="http://mynamespace">
<child />
</root>
In the above example, the default namespace is changed from "" to be http://mynamespace, so now elements that have no prefix are in that default namespace http://mynamespace. To reiterate, the following two documents are identical:
<ns:root xmlns:ns="http://mynamespace">
<ns:child />
</ns:root>
and
<root xmlns="http://mynamespace">
<child />
</root>
Now, what does all of this have to do with your problem?
Well, your element persistenceUnitEl must be in a default namespace that is not "". Somewhere on that element, or on of it's parents, you have something like:
<tagname xmlns="...something....">
<PersistenceUnit>
</PersistenceUnit>
</tagname>
In the above, the PersistenceUnit is in the namespace ...something..... Now, you are asking JDOM to add the element new Element("class") to the document, so you are getting:
<tagname xmlns="...something....">
<PersistenceUnit>
<class xmlns="" />
</PersistenceUnit>
</tagname>
The reason is because you are telling JDOM to put it in the "" namespace (Namespace.NO_NAMESPACE). See the documentation for JDOM here: new Element(String name).
instead, what you want to do, is put it in the same namespace as the parent:
Namespace parentNamespace = persistenceUnitEl.getNamespace();
persistenceUnitEl.add(new Element("class", parentNamespace).addContent(className));
Now, the real question is whether the "class" element actually belongs in the same namespace as the parent, or not. But that is a question only you can answer.
Resources:
Namespace specification
Decent introduction
A tutorial (quite advanced)
JDOM's NamespaceAware documentation
JDOM's FAQ
From my understanding, I think this is what you want.
<RootTagname xmlns="...some namespace....">
<SubTag>
<NewElement yourAttrib="1"/>
</SubTag>
</RootTagname >
This is what you get.
<RootTagname xmlns="...some namespace....">
<SubTag>
<NewElement xmlns="" yourAttrib="1"/>
</SubTag>
</RootTagname >
Use the below snippet to create the new Element
Element newElement = new Element("NewElement", subElement.getNamespace());
Here is the full code.
Namespace namespace = Namespace.getNamespace("prefix", ".....some namespace....");
XPathBuilder<Element> subTagXpathelementBuilder = new XPathBuilder<Element>("//prefix:SubTag", Filters.element());
subTagXpathelementBuilder.setNamespace(namespace);
XPathFactory xpathFactory = XPathFactory.instance();
Document doc = (Document) builder.build(xmlFile);
XPathExpression<Element> xpath = subTagXpathelementBuilder .compileWith(xpathFactory);
List<Element> subElementsList = xpath.evaluate(doc);
for (Element subElement : subElementsList ) {
Element newElement = new Element("NewElement", subElement.getNamespace());
List<Attribute> newElementAttribList = newElement.getAttributes();
newElementAttribList .add(new Attribute("yourAttrib", "1"));
subElement .addContent(newElement);
}

creating a org.w3c.dom.Document object and appending it to a node

I have a xml file like this:
<?xml version="1.0" encoding="utf-8"?>
<Book>
<Author>
XYZ
</Author>
</Book>
I want to add a new node suppose Edition into this like:
<Book>
<Author>
XYZ
</Author>
<Edition>
5
</Edition>
</Book>
How can I do this using java?
I tried doing it as:
In a method I am passing the entire node and new node as a String but its throwing org.apache.xml.dtm.DTMDOMException when I did this:
Document doc = null;
doc = createEmptyDocument(true);
Element child = doc.createElement(childNodeName);
child.setNodeValue(childNodeValue);
node.appendChild(chid);//node is the main node which has all the elements
I'd recommend using something like DOM4J instead of the W3C classes. It adds a layer on top that makes manipulations easier.

How to insert XmlCursor content to DOM Document

Some API returns me XmlCursor pointing on root of XML Document. I need to insert all of this into another org.w3c.DOM represented document.
At start:
XmlCursor poiting on
<a>
<b>
some text
</b>
</a>
DOM Document:
<foo>
</foo>
At the end I want to have original DOM document changed like this:
<foo>
<someOtherInsertedElement>
<a>
<b>
some text
</b>
</a>
</someOtherInsertedElement>
</foo>
NOTE: document.importNode(cursor.getDomNode()) doesn't work - Exception is thrown: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation.
Try something like this:
Node originalNode = cursor.getDomNode();
Node importNode = document.importNode(originalNode.getFirstChild());
Node otherNode = document.createElement("someOtherInsertedElement");
otherNode.appendChild(importNode);
document.appendChild(otherNode);
So in other words:
Get the DOM Node from the cursor. In this case, it's a DOMDocument, so do getFirstChild() to get the root node.
Import it into the DOMDocument.
Do other stuff with the DOMDocument.
Append the imported node to the right Node.
The reason to import is that a node always "belongs" to a given DOMDocument. Just adding the original node would cause exceptions.
I was having the same issue.
This was failing:
Node importNode = document.importNode(originalNode);
This fixed the problem:
Node importNode = document.importNode(originalNode.getFirstChild());

Categories