I am trying to manipulate xsd schema as an xml document that should not be a problem, I believe. But facing troubles with XPath. Whatever XPath I try, it returns nothing. Tried it with or without namespaces but no success.
Please help me understand what am I doing wrong?
My xml is:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.mydomain.com" xmlns="http://www.mydomain.com" elementFormDefault="qualified">
<xs:complexType name="Label">
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element name="Listener"/>
</xs:choice>
</xs:complexType>
</xs:schema>
and application code is:
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setValidating(false);
domFactory.setNamespaceAware(true);
domFactory.setIgnoringComments(true);
domFactory.setIgnoringElementContentWhitespace(true);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("C:/Temp/test.xsd");
// This part works
Node rootNode = dDoc.getElementsByTagName("xs:schema").item(0);
System.out.println(rootNode.getNodeName());
// This part doesn't work
XPath xPath1 = XPathFactory.newInstance().newXPath();
NodeList nList1 = (NodeList) xPath1.evaluate("//xs:schema", dDoc, XPathConstants.NODESET);
System.out.println(nList1.item(0).getNodeName());
// This part doesn't work
XPath xPath2 = XPathFactory.newInstance().newXPath();
NodeList nList2 = (NodeList) xPath2.evaluate("//xs:element", rootNode, XPathConstants.NODESET);
System.out.println(nList2.item(0).getNodeName());
}catch (Exception e){
e.printStackTrace();
}
Set a namespace context using XPath.setNamespaceContext(). This binds the xs prefix to the http://www.w3.org/2001/XMLSchema namespace.
Made changes to your code. It works:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class XPathtest {
public static void main(String[] args) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory
.newInstance();
domFactory.setValidating(false);
domFactory.setNamespaceAware(true);
domFactory.setIgnoringComments(true);
domFactory.setIgnoringElementContentWhitespace(true);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("C:/Temp/test.xsd");
// This part works
Node rootNode = dDoc.getElementsByTagName("xs:schema").item(0);
System.out.println(rootNode.getNodeName());
// This part doesn't work
XPath xPath1 = XPathFactory.newInstance().newXPath();
NamespaceContext nsContext = new NamespaceContext() {
#Override
public String getNamespaceURI(String prefix) {
return "http://www.w3.org/2001/XMLSchema";
}
#Override
public String getPrefix(String namespaceURI) {
return "xs";
}
#Override
public Iterator getPrefixes(String namespaceURI) {
Set s = new HashSet();
s.add("xs");
return s.iterator();
}
};
xPath1.setNamespaceContext((NamespaceContext) nsContext);
NodeList nList1 = (NodeList) xPath1.evaluate("//xs:schema", dDoc,
XPathConstants.NODESET);
System.out.println(nList1.item(0).getNodeName());
// This part doesn't work
// XPath xPath2 = XPathFactory.newInstance().newXPath();
NodeList nList2 = (NodeList) xPath1.evaluate("//xs:element",
rootNode, XPathConstants.NODESET);
System.out.println(nList2.item(0).getNodeName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
The reason is that you haven't specified what xs means. An xml parser must know the namespace url, xs is just an identifier.
You can demonstrate this yourself by using the following code:
XPath xPath = XPathFactory.newInstance().newXPath();
SimpleNamespaceContext nsContext = new SimpleNamespaceContext();
nsContext.addNamespace("t", "http://www.w3.org/2001/XMLSchema");
xPath.setNamespaceContext(nsContext);
xPath.evaluate("//t:schema", dDoc, XPathConstants.NODESET);
You can see that I now use the identifier t instead of xs but that doesn't matter as long as you use the same namespace url.
Related
I am tying to get child node value:
My XML file:
<url-mappings>
<url-mapping url="/screen1">
<request-handler-class>com.mappingtool.rh.Screen1RH</request-handler-class>
</url-mapping>
<url-mapping url="/screen2">
<request-handler-class>com.mappingtool.rh.Screen2RH</request-handler-class>
</url-mapping>
</url-mappings>
My Code is this:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class MappingTool {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(new File("src/com/mappingtool/requestmapping.xml"));
NodeList nodeList = document.getElementsByTagName("url-mapping");
XPath xpath = XPathFactory.newInstance().newXPath();
for(int x=0,size= nodeList.getLength(); x<size; x++) {
System.out.println(nodeList.item(x).getAttributes().getNamedItem("url").getNodeValue());
//System.out.println(nodeList.item(x).getChildNodes().item(0));
//XPathExpression expr = xpath.compile("/request-handler-class/text()");
XPathExpression expr = xpath.compile("/url-mappings/url-mapping/request-handler-class/text()");
//XPathExpression expr = xpath.compile("/url-mappings/url-mapping["+ x +"]/request-handler-class/text()");
String rhClass = (String)expr.evaluate(nodeList.item(x), XPathConstants.STRING);
System.out.println(rhClass);
}
}
}
I am getting output
/screen1
com.mappingtool.rh.Screen1RH
/screen2
com.mappingtool.rh.Screen1RH
But I need
/screen1
com.mappingtool.rh.Screen1RH
/screen2
com.mappingtool.rh.Screen2RH
You can use expression like
XPathExpression expr = xpath.compile("/url-mappings/url-mapping[contains(#url,'"+nodeValue+"')]/request-handler-class/text()"); where nodeValue is value printed by first System.out.println.
I want to manipulate xml doc having default namespace but no prefix. Is there a way to use xpath without namespace uri just as if there is no namespace?
I believe it should be possible if we set namespaceAware property of documentBuilderFactory to false. But in my case it is not working.
Is my understanding is incorrect or I am doing some mistake in code?
Here is my code:
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(false);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("E:/test.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nl = (NodeList) xPath.evaluate("//author", dDoc, XPathConstants.NODESET);
System.out.println(nl.getLength());
} catch (Exception e) {
e.printStackTrace();
}
Here is my xml:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.mydomain.com/schema">
<author>
<book title="t1"/>
<book title="t2"/>
</author>
</root>
The XPath processing for a document that uses the default namespace (no prefix) is the same as the XPath processing for a document that uses prefixes:
For namespace qualified documents you can use a NamespaceContext when you execute the XPath. You will need to prefix the fragments in the XPath to match the NamespaceContext. The prefixes you use do not need to match the prefixes used in the document.
http://download.oracle.com/javase/6/docs/api/javax/xml/namespace/NamespaceContext.html
Here is how it looks with your code:
import java.util.Iterator;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class Demo {
public static void main(String[] args) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("E:/test.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new MyNamespaceContext());
NodeList nl = (NodeList) xPath.evaluate("/ns:root/ns:author", dDoc, XPathConstants.NODESET);
System.out.println(nl.getLength());
} catch (Exception e) {
e.printStackTrace();
}
}
private static class MyNamespaceContext implements NamespaceContext {
public String getNamespaceURI(String prefix) {
if("ns".equals(prefix)) {
return "http://www.mydomain.com/schema";
}
return null;
}
public String getPrefix(String namespaceURI) {
return null;
}
public Iterator getPrefixes(String namespaceURI) {
return null;
}
}
}
Note:
I also used the corrected XPath suggested by Dennis.
The following also appears to work, and is closer to your original question:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class Demo {
public static void main(String[] args) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("E:/test.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nl = (NodeList) xPath.evaluate("/root/author", dDoc, XPathConstants.NODESET);
System.out.println(nl.getLength());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Blaise Doughan is right, attached code is correct.
Problem was somewhere elese. I was running all my tests through Application launcher in Eclipse IDE and nothing was working. Then I discovered Eclipse project was cause of all grief. I ran my class from command prompt, it worked. Created a new eclipse project and pasted same code there, it worked there too.
Thank you all guys for your time and efforts.
I've written a simple NamespaceContext implementation (here), that might be of help. It takes a Map<String, String> as input, where the key is a prefix, and the value is a namespace.
It follows the NamespaceContext spesification, and you can see how it works in the unit tests.
Map<String, String> mappings = new HashMap<>();
mappings.put("foo", "http://foo");
mappings.put("foo2", "http://foo");
mappings.put("bar", "http://bar");
context = new SimpleNamespaceContext(mappings);
context.getNamespaceURI("foo"); // "http://foo"
context.getPrefix("http://foo"); // "foo" or "foo2"
context.getPrefixes("http://foo"); // ["foo", "foo2"]
Note that it has a dependency on Google Guava
i have an xml file that looks like :
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<HWData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NE MOID="WBTS-42" NEType="WBTS">
<EQHO MOID="EQHO-1-0" >
<UNIT MOID="UNIT-FAN-1" State="enabled"></UNIT>
<UNIT MOID="UNIT-FAN-3" State="enabled"></UNIT>
</EQHO>
</NE>
<NE MOID="RNC-40" NEType="RNC">
<EQHO MOID="EQHO-3-0" >
<UNIT MOID="UNIT-FAN-5" State="disabled"></UNIT>
<UNIT MOID="UNIT-FAN-6" State="disabled"></UNIT>
</EQHO>
</NE>
</HWData>
i am asking for how can i get NodeList containing "NE" and "UNIT" tags using DOM ?
thanks
You can do it manually:
import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class XmlDomTest {
public static void main(String[] args) throws Exception {
File file = new File("/path/to/your/file");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);
Set<String> filteredNames = new HashSet<String>(Arrays.asList("NE", "UNIT"));
NodeList list = collectNodes(doc, filteredNames);
for (int i = 0; i < list.getLength(); i++)
System.out.println(list.item(i).getNodeName());
}
private static NodeList collectNodes(Document doc, Set<String> filteredNames) {
Node ret = doc.createElement("NodeList");
collectNodes(doc, filteredNames, ret);
return ret.getChildNodes();
}
private static void collectNodes(Node node, Set<String> filteredNames, Node ret) {
NodeList chn = node.getChildNodes();
for (int i = 0; i < chn.getLength(); i++) {
Node child = chn.item(i);
if (filteredNames.contains(child.getNodeName()))
ret.appendChild(child);
collectNodes(child, filteredNames, ret);
}
}
}
try this :
public static List<String> MOIDList(File file) throws SAXException, IOException, ParserConfigurationException, XPathExpressionException{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression exp = xPath.compile("//NE | //UNIT");
NodeList nl = (NodeList)exp.evaluate(doc, XPathConstants.NODESET);
List<String> MoidList = new ArrayList<>();
for (int i = 0; i < nl.getLength(); i++) {
String moid=((Element)nl.item(i)).getAttribute("MOID");
MoidList.add(moid);
}
return MoidList;
}
The xpath to only select the MOIDS is //NE/#MOID | //UNIT/#MOID.
You should have a look at my open sourced Xml-parser-library unXml. It's available on Maven Central.
You can then do the following:
import com.nerdforge.unxml.Parsing;
import com.nerdforge.unxml.factory.ParsingFactory;
import org.w3c.dom.Document;
import java.util.List;
public class Parser {
public List<String> parseXml(String xml){
Parsing parsing = ParsingFactory.getInstance().create();
Document document = parsing.xml().document(xml);
List<String> result = parsing
.arr("//NE/#MOID | //UNIT/#MOID", parsing.text())
.as(String.class)
.apply(document);
return result;
}
}
parseXml will return the result:
[WBTS-42, UNIT-FAN-1, UNIT-FAN-3, RNC-40, UNIT-FAN-5, UNIT-FAN-6]
You can also create more complex nested datastructures if you need. Give me a comment here, if you want an example on how to do it.
I am trying to use the namespace-uri() function in XPath to retrieve nodes based on their fully qualified name. The query //*[local-name() = 'customerName' and namespace-uri() = 'http://example.com/officeN'] in this online XPath tester, among others, correctly returns the relevant nodes. Yet the following self-contained Java class does not retrieve anything. What am I doing wrong with namespace-uri()?
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class Test{
public static void main(String[] args)throws Exception {
XPathExpression expr = XPathFactory.newInstance().newXPath().compile(
"//*[local-name() = 'customerName' and namespace-uri() = 'http://example.com/officeN']");
String xml=
"<Agents xmlns:n=\"http://example.com/officeN\">\n"+
"\t<n:Agent>\n\t\t<n:customerName>Joe Shmo</n:customerName>\n\t</n:Agent>\n"+
"\t<n:Agent>\n\t\t<n:customerName>Mary Brown</n:customerName>\n\t</n:Agent>\n</Agents>";
System.out.println(xml);
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
System.err.println("\n\nNodes:");
for (int i = 0; i < nodes.getLength(); i++) {
System.err.println(nodes.item(i));
}
}
}
The query looks fine. You also need to declare your DocumentBuilderFactory to be "namespace-aware".
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
Suppose this is the xml:
<lib>
<books type="paperback" name="A" />
<books type="pdf" name="B" />
<books type="hardbound" name="A" />
</lib>
What will be the xpath code to search for book of type="paperback" and name="A"? TIA.
Currently my code looks like this:
import org.w3c.dom.*;
import javax.xml.xpath.*;
import javax.xml.parsers.*;
import java.io.IOException;
import org.xml.sax.SAXException;
public class demo {
public static void main(String[] args)
throws ParserConfigurationException, SAXException,
IOException, XPathExpressionException {
DocumentBuilderFactory domFactory =
DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("xml.xml");
XPath xpath = XPathFactory.newInstance().newXPath();
// XPath Query for showing all nodes value
String version="fl1.0";
XPathExpression expr = xpath.compile("//books/type[#input="paperback"]/text()");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
}
}
}
/lib/books[#type='paperback' and #name='A']
Have a look here if you're struggling with xpath syntax, it has a few nice examples.
Also, if you just need help with XML in general and related technologies, have a look at the guide here
Right now you seem to search for something like <book><type input="paperback"/></book> which clearly is wrong. Your xpath expression should probably be something like //books[#type="paperback" and #name="A"]/text()
The answers above are good...I wanted to contribute a resource. The best xpath tutorial I've seen is here.