Dom4j selectNodes(arg) don't give list of nodes - java

I am using DOM4j for XML work in java,
my xml is like this:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<abcd name="ab.catalog" xmlns="http://www.xyz.com/pqr" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.xyz.com/pqr ./abc.xyz.xsd">
<efg>
......
</efg>
<efg>
.....
</efg>
</abcd>
then,
List<Node>list = document.selectNodes("/abcd/efg");
gets the size of list zero. I feel it's due to namespace specified in the xml.
I tried a lot but cn't get success.

Unprefixed element names in XPath expressions refer to elements that are not in a namespace - they do not take account of the "default" xmlns="..." namespace declared on the document. You need to declare a prefix for the namespace in the XPath engine and then use that prefix in the expression. Here is an example inspired by the DOM4J javadocs:
Map uris = new HashMap();
uris.put("pqr", "http://www.xyz.com/pqr");
XPath xpath = document.createXPath("/pqr:abcd/pqr:efg");
xpath.setNamespaceURIs(uris);
List<Node> nodes = xpath.selectNodes(document);

Modify your code :
List<Node>list = document.selectNodes("//abcd/efg");

Related

Remove xmlns attribute

I have an xml
<XML>
<Client xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="2.34.00" ></Client>
</XML>
How to remove all the attributes in the Client tag in Java.
I used earlier
Element element = (Element) doc.getElementsByTagName("Client").item(0);
element.removeAttribute("Version");
element.removeAttribute("xmlns:xsi");
The Version attribute was removed from the Client tag. But xmlns:xsi attribute was NOT removed.
Does anybody faced any similar issue or know how to approach this problem.
You should getOwnerDocument() for node "Client" and do renameNode(nodeClient,null,"Client");
NB: removeAttribute removes "xmlns:xsi", but namespace is stored in Node "Client" and can appear at Document to string regeneration moment.

JDOM2 - two Namespaces

I am trying to build the following XML structure:
<EDIOrderPackage xmlns="urn:URI" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Version>1.0.0.0</Version>
<Test>true</Test>
</EDIOrderPackage>
I use JDOM2 and don't know how to add 2 Namespaces!
Even if I set only one Namespace the result is not the same as I wish it to be.
If I set the Namespace by root.setNamespace() and use the 2nd one with the prefix i it looks like this:
<i:EDIOrderPackage mlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Version>1.0.0.0</Version>
<Test>true</Test>
<i:/EDIOrderPackage>
So there is an i before the EDIPOrderPackage.
If i dont use a prefix is looks like this:
<EDIOrderPackage xmlns="urn:URI">
<Version xmlns="">1.0.0.0</Version>
<Test xmlns="">true</Test>
</EDIOrderPackage>
If if try to add it as attributes, it throws the error message that I cannot an attribute with the name "xmlns"
So how can I build an XML with JDOM looking like the one above?
The trick is that, with Namespaces, you have to specify it correctly for all elements you add.
Additionally, a default Namespace is one that is declared as xmlns="...." and not xmlns:abc="...."
When you use a default namespace, it has no 'prefix' on the elements. So from your example code you have:
The default namespace: xmlns="urn:URI"
The 'i' namespace: xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
You can create these with JDOM as:
Namespace nsDefault = Namespace.getNamespace("urn:URI");
Namespace nsI = Namespace.getNamespace("i", "http://www.w3.org/2001/XMLSchema-instance");
Now, when you create your elements, you have to put them in the right Namespaces:
Element root = new Element("EDIOrderPackage", nsDefault);
Element version = new Element("Version", nsDefault);
Element test = new Element("Test", nsDefault);
root.addNamespaceDeclaration(nsI); // add the i namespace declaration.
root.addContent(version);
root.addContent(test);
If you add the XMLOutputter aspect of things:
Document doc = new Document(root);
XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
xout.output(doc, System.out);
the above code produces the output
<?xml version="1.0" encoding="UTF-8"?>
<EDIOrderPackage xmlns="urn:URI" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Version />
<Test />
</EDIOrderPackage>

What is the XPath expression to select text from orm.xml's <schema> element?

I've read XPath - how to select text and thought I had the general idea. But, as always, XPath rears up, hisses at me, and scuttles off to find the nearest bacteria-infested urinal to drown in.
I have a JPA orm.xml file. It looks like this:
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
version="2.0">
<persistence-unit-metadata>
<persistence-unit-defaults>
<schema>test</schema>
<catalog>test</catalog>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>
The following XPath expression should, I would think, select the text from the <schema> element:
/entity-mappings/persistence-unit-metadata/persistence-unit-defaults/schema/text()
But using Java's XPath implementation, it does not.
More specifically, the following code fails (using JUnit asserts) on the last line. The value of the text variable is the empty string.
// Find the file: URL to the orm.xml I mentioned above.
final URL ormUrl = Thread.currentThread().getContextClassLoader().getResource("META-INF/orm.xml");
assertNotNull(ormUrl);
final XPathFactory xpf = XPathFactory.newInstance();
assertNotNull(xpf);
final XPath xpath = xpf.newXPath();
assertNotNull(xpath);
final XPathExpression expression = xpath.compile("/entity-mappings/persistence-unit-metadata/persistence-unit-defaults/schema/text()");
assertNotNull(expression);
final String text = expression.evaluate(new InputSource(ormUrl.openStream()));
assertEquals("test", text);
This seems to cast into doubt what little understanding I had of XPath expressions to begin with. Flailing around, I then wanted to see if a simple "/" would select the root element. Mercifully, this returned a non-null NodeList, but the NodeList was empty. I really don't want to hunt the authors of the Java XPath support down and string them up, but it's getting awfully difficult not to follow that course of action.
Please help me shoot XPath in the head once and for all. Thanks.
The problem is that the XML declares a default namespace
xmlns="http://java.sun.com/xml/ns/persistence/orm"
while in your XPath expression you have not provided a corresponding namespace context. See this link for details on how to work with namespace contexts. There's a lot of detail there, but in summary you have to write your own implementation of javax.xml.namespace.NamespaceContext that allows the XPath processor to map namespace prefixes to URIs. In your case you must provide a mapping for the default namespace to the appropriate URI.

How to set namespace only on first tag with XOM?

I am using XOM to build XML documents in Java.
I have created a simple XML document, and I want an XML namespace. But when I set the namespace on the first tag, an empty namespace is set on the childs like xmlns="", how can I get rid of this behaviour? I only want xmlns on the first tag.
I want this XML:
<request xmlns="http://my-namespace">
<type>Test</type>
<data>
<myData>test data</myData>
</data>
</request>
But this is the XML document output from XOM
<request xmlns="http://my-namespace">
<type xmlns="">Test</type>
<data xmlns="">
<myData>test data</myData>
</data>
</request>
This is my Java XOM code:
String namespace = "http://my-namespace";
Element request = new Element("request", namespace);
Element type = new Element("type");
type.appendChild("Test");
request.appendChild(type);
Element data = new Element("data");
request.appendChild(data);
Element myData = new Element("myData");
myData.appendChild("test data");
data.appendChild(myData);
Document doc = new Document(request);
doc.toXML();
This works for me. However, I'm a bit puzzled as to why the Element objects don't inherit the namespace of their parents, though. (Not an XML nor XOM expert)
Code:
String namespace = "http://my-namespace";
Element request = new Element("request", namespace);
Element type = new Element("type", namespace);
type.appendChild("Test");
request.appendChild(type);
Element data = new Element("data", namespace);
request.appendChild(data);
Element myData = new Element("myData", namespace);
myData.appendChild("test data");
data.appendChild(myData);
Document doc = new Document(request);
System.out.println(doc.toXML());
Output:
<?xml version="1.0"?>
<request xmlns="http://my-namespace">
<type>Test</type>
<data>
<myData>test data</myData>
</data>
</request>
I ran into the same problem, and Google lead me here.
#Michael - That's what it says in the javadoc, yes, but unfortunately, that's not how it works when you implement it. The child elements will continue to get blank xmlns attributes unless you do Catchwa's implementation.
Catchwa's implementation works just fine. Only the element I tell it to have a namespace, has a namespace. All empty xmlns attributes are gone. It's strange.
Is it a bug? I can't seem to figure that part out. Or is it just the way XOM works?
Don't confuse namespaces and namespace declarations. The namespace is an intrinsic property of each element. The namespace declaration is the `xmlns' attribute. They are not the same thing, although they are connected. When you create an element, you set its namespace, not its namespace declaration.
In the XOM data model namespaces are not attributes. They are an intrinsic property of the element itself. There is no rule in XML that requires children of an element to be in the same namespace as the parent. Indeed theoretically every element in the document could be in a different namespace.
In XOM you specify the namespace of an element or attribute at the same time you specify the local name. When you create an element, the element initially has no parent so there's no way for XOM to default to giving the element the same namespace as its parent, even if that's what was wanted (and it's not).
When the document is serialized the namespaces are represented by xmlns and xmlns:*prefix* attributes. XOM figures out where to put these elements to match the namespaces you've assigned to each element. Just specify the namespace you want for each element in your code, and let XOM figure out where to put the namespace declarations.
In XOM you can add a namespace declaration to the root element.
Here's a short example with three different namespaces:
final String NS_XLINK = "http://www.w3.org/1999/xlink";
final String NS_OTHER = "http://other.com";
Element root = new Element("root", "http://root.com");
root.addNamespaceDeclaration("xlink", NS_XLINK);
root.addNamespaceDeclaration("other", NS_OTHER);
root.addAttribute(new Attribute("xlink:href", NS_XLINK, "http://somewhere.com"));
root.appendChild(new Element("other:alien", NS_OTHER));
Document doc = new Document(root);
System.out.println(doc.toXML());
which produces this result (with additional line breaks inserted for readability):
<?xml version="1.0"?>
<root
xmlns="http://root.com"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:other="http://other.com"
xlink:href="http://somewhere.com">
<other:alien />
</root>

How do I select something with a blank namespace with Jaxen?

I have the following xml:
<config xmlns="http://www.someurl.com">
<product>
<brand>
<content />
</brand>
</product>
</config>
I'm reading it nicely into JDOM.
However, when I try to use Jaxen to grab the contents, I can't seem to get anything.
Here's an example of what doesn't seem to work:
XPath xpath = new JDOMXPath("config");
SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
namespaceContext.addNamespace("", "http://www.someurl.com");
xpath.setNamespaceContext(namespaceContext);
assert xpath.selectNodes(document).size() > 0 : "should find more than 0";
This assertion always fails.
What am I doing wrong?
You have to assign a prefix. Make that call addNamespace("hopfrog", "http://...");
Then make the XPath ("hopfrog:config");
Keep in mind that the prefixes in XML aren't part of the real data model. The real data model assigns a URL, possibly blank, to each element and attribute. You can use any prefix you want in XPath so long as it's bound to the right URL. Since the URL you want it blank, you bind a prefix to 'blank'.

Categories

Resources