Incoherent XPath output - java

I just discovered XPath and I'm trying to use it in order to parse an XML file. I read a few courses about it, but I am stuck with a problem. When I try to get a NodeList from the file, the getLength() method always returns 0.
However, when I try a
document.getElementsByTagName("crtx:env").getLength()
The output is correct (7 in my case).
I do not really understand, because my nodelist is built according to my Document, the output should be similar, isn't it ?
Here is a part of my code :
IFile f = (IFile) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getEditorInput().getAdapter(IFile.class);
String fileURL = f.getLocation().toOSString();
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = builderFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
Document document = null;
if (builder != null){
try{
document = builder.parse(fileURL);
System.out.println("DOCUMENT URI : " + document.getDocumentURI());
} catch (Exception e){
e.printStackTrace();
}
} else {
System.out.println("builder null");
}
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodeList = null;
try {
nodeList = (NodeList) xPath.compile("crtx:env").evaluate(document, XPathConstants.NODESET);
} catch (XPathExpressionException e) {
e.printStackTrace();
}
System.out.println("NODELIST SIZE : " + nodeList.getLength());
System.out.println(document.getElementsByTagName("crtx:env").getLength());
}
The first System.out.println() returns coherent output (a good URI), but the two last lines return a different number.
Any help would be appreciated.
Thanks for reading.

XPath is defined for XML with namespaces so set
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
Then to use a path with a namespace prefix you need to use https://docs.oracle.com/javase/7/docs/api/javax/xml/xpath/XPath.html#setNamespaceContext%28javax.xml.namespace.NamespaceContext%29 to bind the prefix(es) used to namespace URIs, see https://xml.apache.org/xalan-j/xpath_apis.html#namespacecontext for an example
With a namespace aware DOM you will need to change your getElementsByTagName call to use getElementsByTagNameNS however.

Related

Get value of XML tag using java

I'm trying to read the value of "release" tag of a remote XML file and return it's value .I'm able to find the value of "release" tag using getElementText() but not by getElementValue()
Java Code..
try {
URL url1 = new URL("http://hsv-artifactory.emrsn.org:8081/artifactory/libs-release-local/com/avocent/commonplatform/cps/symbols/gdd/GDDResources/maven-metadata.xml");
XMLStreamReader reader1 = XMLInputFactory.newInstance().createXMLStreamReader(url1.openStream());
String Latest = null;
while (reader1.hasNext()) {
if (reader1.next() == XMLStreamConstants.START_ELEMENT) {
if (reader1.getLocalName().equals("release")) {
Latest = reader1.getElementText();
break;
}
}
}
System.out.println("Latest version in Artifactory is :"+Latest);
} catch (IOException ex) {
// handle exception
Logger.getLogger(SVNRepoConnector1.class.getName()).log(Level.SEVERE, null, ex);
} catch (XMLStreamException ex) {
// handle exception
Logger.getLogger(SVNRepoConnector1.class.getName()).log(Level.SEVERE, null, ex);
} finally {
// close the stream
}
In the above code the value is being stored in a String variable but i want to store it in an integer variable so that i can perform operations like addition,subtraction on it afterwards..Please Help
DOM Solution:
URL url1 = new URL("http://hsv-artifactory.emrsn.org:8081/artifactory/libs-release-local/com/avocent/commonplatform/cps/symbols/gdd/GDDResources/maven-metadata.xml");
InputSource xmlInputSource = new InputSource(url1.openStream());
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document dom = dBuilder.parse(xmlInputSource);
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("//release");//all release elements
NodeList nodes = (NodeList) expr.evaluate(e,XPathConstants.NODESET);
ArrayList<Integer> releaseList = new ArrayList<Integer>();
for (int i = 0; i < nodes.getLength(); i++) {
Element releaseElem = (Element) nodes.item(i);
releaseList.add(Integer.parseInt(releaseElem.getText()));
}
Just catch the exceptions.

Read inside a Tag using XPath Java

Hye I am new to read XML File using Java my problem is that I have been trying to read an xml and between a specific tag I want to get the required data I am using XPath and my query is:
String expression = "/ADOXML/MODELS/MODEL/MODELATTRIBUTES/ATTRIBUTE[#type='STRING']";
It works fine and my specific Tag to read from is:
<ATTRIBUTE name="Description" type="STRING"> SOME TEXT </ATTRIBUTE>
But I want to read the data inside only these types of Tags so that my output should be:
SOME TEXT
inside the tag!
can somebody help me how can I do this Please I am new to xml reading! Trying my best as:
String expression = "/ADOXML/MODELS/MODEL/MODELATTRIBUTES/ATTRIBUTE[#name='Description' and ./type/text()='STRING']";
But it wont give me any output!
thanks in advance
My Code:
DocumentBuilderFactory builderFactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = builderFactory.newDocumentBuilder();
org.w3c.dom.Document document = builder.parse(
new FileInputStream("c:\\y.xml"));
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/ADOXML/MODELS/MODEL/MODELATTRIBUTES/ATTRIBUTE[#name='Description'and #type='STRING']";
System.out.println(expression);
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
System.out.println(nodeList.item(i).getFirstChild().getNodeValue());
}
} catch (ParserConfigurationException | SAXException | IOException e) {
System.out.print(e);
}
There is a problem with my code cant figure out what!
This code works fine for me with the changed XPath to:
"/ADOXML/MODELS/MODEL/MODELATTRIBUTES/ATTRIBUTE[#name='Description'][#type='STRING']":
private static final String EXAMPLE_XML =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<ADOXML adoversion=\"Version 5.1\" username=\"kvarga\" database=\"adonisdb\" time=\"08:55\" date=\"30.11.2013\" version=\"3.1\">" +
"<MODELS>" +
"<MODEL version=\"\" applib=\"ADONIS BPMS BP Library 5.1\" libtype=\"bp\" modeltype=\"Business process model\" name=\"Product development\" id=\"mod.25602\">" +
"<MODELATTRIBUTES>" +
"<ATTRIBUTE name=\"Version number\" type=\"STRING\"> </ATTRIBUTE>" +
"<ATTRIBUTE name=\"Author\" type=\"STRING\">kvarga</ATTRIBUTE>" +
"<ATTRIBUTE name=\"Description\" type=\"STRING\">I WANT THIS PARA 2</ATTRIBUTE>" +
"</MODELATTRIBUTES>" +
"</MODEL>" +
"</MODELS>" +
"</ADOXML>";
public static void main(String[] args) {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = builderFactory.newDocumentBuilder();
Document document = builder.parse(new ByteArrayInputStream(EXAMPLE_XML.getBytes()));
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/ADOXML/MODELS/MODEL/MODELATTRIBUTES/ATTRIBUTE[#name='Description'][#type='STRING']";
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
System.out.println("###" + nodeList.item(i).getFirstChild().getNodeValue() + "###");
}
} catch (Exception e) {
System.out.print(e);
}
}
OUTPUT:
###I WANT THIS PARA 2###
The mentioned code works fine.
You can try other way also to get the text node -
String expression = "/ADOXML/MODELS/MODEL/MODELATTRIBUTES/ATTRIBUTE/text()";
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(document, XPathConstants.NODESET);
System.out.println(nodeList.item(0).getNodeValue());

Cannot create XML Document from String

I am trying to create an org.w3c.dom.Document form an XML string. I am using this How to convert string to xml file in java as a basis. I am not getting an exception, the problem is that my document is always null. The XML is system generated and well formed. I wish to convert it to a Document object so that I can add new Nodes etc.
public static org.w3c.dom.Document stringToXML(String xmlSource) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream input = IOUtils.toInputStream(xmlSource); //uses Apache commons to obtain InputStream
BOMInputStream bomIn = new BOMInputStream(input); //create BOMInputStream from InputStream
InputSource is = new InputSource(bomIn); // InputSource with BOM removed
Document document = builder.parse(new InputSource(new StringReader(xmlSource)));
Document document2 = builder.parse(is);
System.out.println("Document=" + document.getDoctype()); // always null
System.out.println("Document2=" + document2.getDoctype()); // always null
return document;
}
I have tried these things: I created a BOMInputStream thinking that a BOM was causing the conversion to fail. I thought that this was my issue but passing the BOMInputStream to the InputSource doesn't make a difference. I have even tried passing a literal String of simple XML and nothing but null. The toString method returns [#document:null]
I am using Xpages, a JSF implementation that uses Java 6. Full name of Document class used to avoid confusion with Xpage related class of the same name.
Don't rely on what toString is telling you. It is providing diagnostic information that it thinks is useful about the current class, which is, in this case, nothing more then...
"["+getNodeName()+": "+getNodeValue()+"]";
Which isn't going to help you. Instead, you will need to try and transform the model back into a String, for example...
String text
= "<fruit>"
+ "<banana>yellow</banana>"
+ "<orange>orange</orange>"
+ "<pear>yellow</pear>"
+ "</fruit>";
InputStream is = null;
try {
is = new ByteArrayInputStream(text.getBytes());
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(is);
System.out.println("Document=" + document.toString()); // always null
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.setOutputProperty(OutputKeys.METHOD, "xml");
tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
ByteArrayOutputStream os = null;
try {
os = new ByteArrayOutputStream();
DOMSource domSource = new DOMSource(document);
StreamResult sr = new StreamResult(os);
tf.transform(domSource, sr);
System.out.println(new String(os.toByteArray()));
} finally {
try {
os.close();
} finally {
}
}
} catch (ParserConfigurationException | SAXException | IOException | TransformerConfigurationException exp) {
exp.printStackTrace();
} catch (TransformerException exp) {
exp.printStackTrace();
} finally {
try {
is.close();
} catch (Exception e) {
}
}
Which outputs...
Document=[#document: null]
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<fruit>
<banana>yellow</banana>
<orange>orange</orange>
<pear>yellow</pear>
</fruit>
You can try using this: http://www.wissel.net/blog/downloads/SHWL-8MRM36/$File/SimpleXMLDoc.java

How to retrieve XML including tags using the DOM parser

I am using org.w3c.dom to parse an XML file. Then I need to return the ENTIRE XML for a specific node including the tags, not just the values of the tags. I'm using the NodeList because I need to count how many records are in the file. But I also need to read the file wholesale from the beginning and then write it out to a new XML file. But my current code only prints the value of the node, but not the node itself. I'm stumped.
public static void main(String[] args) {
try {
File fXmlFile = new File (args[0]);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(fXmlFile);
System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
NodeList listOfRecords = doc.getElementsByTagName("record");
int totalRecords = listOfRecords.getLength();
System.out.println("Total number of records : " + totalRecords);
int amountToSplice = queryUser();
for (int i = 0; i < amountToSplice; i++) {
String stringNode = listOfRecords.item(i).getTextContent();
System.out.println(stringNode);
}
} catch (Exception e) {
e.printStackTrace();
}
}
getTextContent() will only "return the text content of this node and its descendants" i.e. you only get the content of the 'text' type nodes. When parsing XML it's good to remember there are several different types of node, see XML DOM Node Types.
To do what you want, you could create a utility method like this...
public static String nodeToString(Node node)
{
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter sw = new StringWriter();
t.transform(new DOMSource(node), new StreamResult(sw));
return sw.toString();
}
Then loop and print like this...
for (int i = 0; i < amountToSplice; i++)
System.out.println(nodeToString(listOfRecords.item(i)));

Simplest way to parse this XML in Java?

I have the following XML:
<ConfigGroup Name="Replication">
<ValueInteger Name="ResponseTimeout">10</ValueInteger>
<ValueInteger Name="PingTimeout">2</ValueInteger>
<ValueInteger Name="ConnectionTimeout">10</ValueInteger>
<ConfigGroup Name="Pool">
<ConfigGroup Name="1">
<ValueString Encrypted="false" Name="Host">10.20.30.40</ValueString>
<ValueInteger Name="CacheReplicationPort">8899</ValueInteger>
<ValueInteger Name="RadiusPort">12050</ValueInteger>
<ValueInteger Name="OtherPort">4868</ValueInteger>
</ConfigGroup>
<ConfigGroup Name="2">
<ValueString Encrypted="false" Name="Host">10.20.30.50</ValueString>
<ValueInteger Name="CacheReplicationPort">8899</ValueInteger>
<ValueInteger Name="RadiusPort">12050</ValueInteger>
<ValueInteger Name="OtherPort">4868</ValueInteger>
</ConfigGroup>
</ConfigGroup>
</ConfigGroup>
I just wondering what is the simplest way to parse this XML in Java - I want the value from the two host elements (e.g. 10.20.30.40 and 10.20.30.50). Note there may be more than two pool entries (or none at all).
I'm having trouble finding a simple example of how to use the various XML parsers for Java.
Any help is much appreciated.
Thanks!
The simplest way to search for what you are looking for, would be XPath.
try {
//Load the XML File
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document configuration = builder.parse("configs.xml");
//Create an XPath expression
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
XPathExpression expr = xpath.compile("//ConfigGroup/ValueString[#Name='Host']/text()");
//Execute the XPath query
Object result = expr.evaluate(configuration, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
//Parse the results
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
}
} catch (ParserConfigurationException e) {
System.out.println("Bad parser configuration");
e.printStackTrace();
} catch (SAXException e) {
System.out.println("SAX error loading the file.");
e.printStackTrace();
} catch (XPathExpressionException e) {
System.out.println("Bad XPath Expression");
e.printStackTrace();
} catch (IOException e) {
System.out.println("IO Error reading the file.");
e.printStackTrace();
}
The XPath expression
"//ConfigGroup/ValueString[#Name='Host']/text()"
looks for ConfigGroup elements anywhere in your XML, then finds ValueString elements within the ConfigGroup elements, that have a Name attribute with the value "Host". #Name=Host is like a filter for elements with the name ValueString. And text() at the end, returns the text node of the selected elements.
Java XPath API allows to do it easily. The following xpath expression
//ValueString[#Name='Host']
should match what you want. Here is how to use it with the API :
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(yourXml.getBytes());
XPath xpath = XPathFactory.newInstance().newXPath();
NodeList nodeList = (NodeList) xpath.compile("//ValueString[#Name='Host']").evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
String ip = ((Element) nodeList.item(i)).getTextContent();
// do something with your ip
}
You could use SAXON
String vs_source = "Z:/Code_JavaDOCX/1.xml";
Processor proc = new Processor(false);
net.sf.saxon.s9api.DocumentBuilder builder = proc.newDocumentBuilder();
XPathCompiler xpc = proc.newXPathCompiler();
try{
XPathSelector selector = xpc.compile("//output").load();
selector.setContextItem(builder.build(new File(vs_source)));
for (XdmItem item: selector)
{
System.out.println(item.getStringValue());
}
}
catch(Exception e)
{
e.printStackTrace();
}

Categories

Resources