How to use variables in XPath?
Solution 1
This XPATH will select all the elements within the prop
element that follows the OBJECT_TYPE
with the text SiteData
:
//OBJECT_TYPE[text() = 'SiteData']/following-sibling::prop[1]/*
To change the OBJECT_TYPE
being selected just construct the XPATH in the code:
String xpath = "//OBJECT_TYPE[text() = '" + getObjType() + "']/following-sibling::prop[1]/*"
Which results in code like this:
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodeList = (NodeList)xPath.compile("//OBJECT_TYPE[text() = '" + getObjType() + "']/following-sibling::prop[1]/*").evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++)
{
System.out.println(nodeList.item(i).getNodeName() + " = " + nodeList.item(i).getTextContent());
}
That given the XML from the question and when getObjType()
returns SiteData
prints:
DESCRIPTION = Site parameters
PARENT = NULL
VIRTUAL = 0
VISIBLE = 1
PICTURE = NULL
HELP = 10008
MIN_NO = 1
MAX_NO = 1
NAME_FORMAT = NULL
Solution 2
If you want to extract the prop
belonging to a specific OBJECT_TYPE
you can do that with
/type/OBJECT_TYPE[. = 'some type']/following-sibling::prop[1]
In Java you could build up this XPath expression dynamically using string concatenation but it would be much safer to use an XPath variable if the library you're using can support that (you don't say in the question what library you're using). For example with javax.xml.xpath
XPath xp = XPathFactory.newInstance().newXPath();
final Map<String, Object> vars = new HashMap<String, Object>();
xp.setXPathVariableResolver(new XPathVariableResolver() {
public Object resolveVariable(QName name) {
return vars.get(name.getLocalPart());
}
});
XPathExpression expr = xp.compile("/type/OBJECT_TYPE[. = $type]/following-sibling::prop[1]");
vars.put("type", "Data");
Node dataProps = (Node)expr.evaluate(doc, XPathConstants.NODE);
vars.put("type", "SiteData");
Node siteProps = (Node)expr.evaluate(doc, XPathConstants.NODE);
// taking the value from a variable
vars.put("type", obj.getObjectType());
Node objectProps = (Node)expr.evaluate(doc, XPathConstants.NODE);
![Sembrano](https://i.stack.imgur.com/8O65n.jpg?s=256&g=1)
Sembrano
There are no stupid questions. Just unclear questions.
Updated on January 22, 2020Comments
-
Sembrano over 4 years
I am using Xpath and Java.
The XML got plenty of
OBJECT_TYPES
and every object type has properties and parameters. And each property and parameter got elements.How do I do the following from my XML file. I wanna know how to select with the XPATH string expression all property elements depending on whats the name of the
OBJECT_TYPE
string. The object type string name depends on what name the user selects from the list.How can I do that?
Should be something like :
String expression = "/getObjType()/prop/*";
But the
getObjectType
is a method so I cant use it in a string expression.XML looks something like this:
<type> <OBJECT_TYPE>SiteData</OBJECT_TYPE> <prop> <DESCRIPTION>Site parameters</DESCRIPTION> <PARENT>NULL</PARENT> <VIRTUAL>0</VIRTUAL> <VISIBLE>1</VISIBLE> <PICTURE>NULL</PICTURE> <HELP>10008</HELP> <MIN_NO>1</MIN_NO> <MAX_NO>1</MAX_NO> <NAME_FORMAT>NULL</NAME_FORMAT> </prop> <param> <PARAMETER>blabla</PARAMETER> <DATA_TYPE>INTEGER</DATA_TYPE> <DESCRIPTION>blaba</DESCRIPTION> <MIN_NO>1</MIN_NO> <MAX_NO>1</MAX_NO> <ORDER1>1</ORDER1> <NESTED>0</NESTED> <DEFAULT1>NULL</DEFAULT1> <FORMAT>0:16382</FORMAT> </param> <OBJECT_TYPE>Data</OBJECT_TYPE> <prop> <DESCRIPTION>Site parameters</DESCRIPTION> <PARENT>NULL</PARENT> <VIRTUAL>0</VIRTUAL> <VISIBLE>1</VISIBLE> <PICTURE>NULL</PICTURE> <HELP>10008</HELP> <MIN_NO>1</MIN_NO> <MAX_NO>1</MAX_NO> <NAME_FORMAT>NULL</NAME_FORMAT> </prop> <param> <PARAMETER>gmgm</PARAMETER> <DATA_TYPE>INTEGER</DATA_TYPE> <DESCRIPTION>babla</DESCRIPTION> <MIN_NO>1</MIN_NO> <MAX_NO>1</MAX_NO> <ORDER1>1</ORDER1> <NESTED>0</NESTED> <DEFAULT1>NULL</DEFAULT1> <FORMAT>0:16382</FORMAT> </param> </type>
So depending on whats the name of the Object_type I wanna get thoose properties and I have list 122 object types so I have to use a varible to pick which one the user selects.
public class PropXMLParsing { static PropXMLParsing instance = null; private List<String> list = new ArrayList<String>(); ObjType obj = new ObjType(); public static PropXMLParsing getInstance() { if (instance == null) { instance = new PropXMLParsing(); try { instance.ParserForObjectTypes(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } } return instance; } public void ParserForObjectTypes() throws SAXException, IOException, ParserConfigurationException { try { FileInputStream file = new FileInputStream(new File( "xmlFiles/CoreDatamodel.xml")); DocumentBuilderFactory builderFactory = DocumentBuilderFactory .newInstance(); builderFactory.setNamespaceAware(true); DocumentBuilder builder = builderFactory.newDocumentBuilder(); Document xmlDocument = builder.parse(file); XPath xp = XPathFactory.newInstance().newXPath(); final Map<String, Object> vars = new HashMap<String, Object>(); xp.setXPathVariableResolver(new XPathVariableResolver() { public Object resolveVariable(QName name) { return vars.get(name.getLocalPart()); } }); XPathExpression expr = xp .compile("/type/OBJECT_TYPE[. = $type]/following-sibling::prop[1]"); vars.put("type", obj.getObjectType()); NodeList objectProps = (NodeList) expr.evaluate(xmlDocument, XPathConstants.NODESET); System.out.println(objectProps); for (int i = 0; i < objectProps.getLength(); i++) { System.out.println(objectProps.item(i).getFirstChild() .getNodeValue()); list.add(objectProps.item(i).getFirstChild().getNodeValue()); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (XPathExpressionException e) { e.printStackTrace(); } } public String convertListToString() { StringBuilder sb = new StringBuilder(); if (list.size() > 0) { sb.append(list.get(0)); for (int i = 1; i < list.size(); i++) { sb.append(list.get(i)); } } return sb.toString(); } }
Second solution I have tried that aint working neither not printing out anything in the console.
public void ParserForObjectTypes() throws SAXException, IOException, ParserConfigurationException { try { FileInputStream file = new FileInputStream(new File( "xmlFiles/CoreDatamodel.xml")); DocumentBuilderFactory builderFactory = DocumentBuilderFactory .newInstance(); DocumentBuilder builder = builderFactory.newDocumentBuilder(); Document xmlDocument = builder.parse(file); XPath xPath = XPathFactory.newInstance().newXPath(); NodeList nodeList = (NodeList) xPath.compile( "//OBJECT_TYPE[text() = '" + obj.getObjectType() + "']/following-sibling::prop[1]/*").evaluate( xmlDocument, XPathConstants.NODESET); for (int i = 0; i < nodeList.getLength(); i++) { System.out.println(nodeList.item(i).getNodeName() + " = " + nodeList.item(i).getTextContent()); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (XPathExpressionException e) { e.printStackTrace(); } }
-
Nick Holt over 10 yearsWhat's with the downvote - that XPath works and solves the question.
-
Sembrano over 10 yearsYes but in my case we dont know the name is SiteData unless the user clicks on it in the gui therefore all is stored in a variable and that I have getters and setters for. Ive done something like this but havent tested it yet : String expression = "/" + obj.getObjectType() + "/prop/*";
-
Sembrano over 10 yearsYes but in my case we dont know the name is SiteData unless the user clicks on it in the gui therefore all is stored in a variable and that I have getters and setters for and also there are like 122 different object types. Ive done something like this but havent tested it yet : String expression = "/" + obj.getObjectType() + "/prop/*";
-
Ian Roberts over 10 years@Sembrano you can provide anything you like to the
vars.put("type", ...)
call. I would always advise you not to build up your XPath expressions by concatenating together user-supplied strings, it's the inviting XPath equivalent of an SQL-injection attack. -
Nick Holt over 10 yearsThat won't work, the string concatenation in the example in my answer will where
getObjType()
returnsSiteData
or one of the other valid values will. If the XPATH is run before the user has selected an object type, then a default value must be set else the XPATH may be invalid. -
Nick Holt over 10 yearsIs there such a thing as an XPath-injection attack? Suppose you could return nodes you're not meant to see - is that what you mean?
-
Ian Roberts over 10 years@NickHolt maybe "attack" is a bit strong but essentially if you allow user-provided input to be part of your expression then you have no guarantee it will even compile - the classic example for XPath is that there's no way to include both single and double quotes within one string literal (double quoted strings can contain single quotes and vice versa, but there's no escaping mechanism to allow one string to contain both types). With a variable resolver the actual XPath expression is a compile time constant - while it may match nothing at least you know it won't throw an exception.
-
Nick Holt over 10 yearsI see, interesting, though the question does mention selecting from a list, rather than the user typing the 'type' explicitly.
-
Sembrano over 10 yearsThis code prints out null : dataProps : null siteProps : null objectProps : null
-
Ian Roberts over 10 years@Sembrano in order to use XPath you have to parse your document with a namespace-aware parser - call
builderFactory.setNamespaceAware(true);
before you dobuilderFactory.newDocumentBuilder()
. Apart from that it should work, assuming the XML you gave in the question is a true representation of the real structure. -
Ian Roberts over 10 years@Sembrano the code you posted does not call
setNamespaceAware(true)
-
Sembrano over 10 yearsI added it now and updated the code stil not working. It should print 9 contents from the xml but it print only a object : com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList@1431340
-
Ian Roberts over 10 years@Sembrano right, that looks like you've got a node list there, so you're now out of XPath and back into the Java DOM APIs to iterate over that list and extract whatever you need from it. The XPath expression I have given you will find the right
props
element (so I suspect that node set contains one node), if you add/*
to the end of it you'll get the elements inside theprops
instead, either way you'll need to use DOM APIs from this point on. -
Sembrano over 10 yearsyeh i want to save it in a string[] actually but didnt get it working so I put it in a list since I got one working. Anyhow I added /* at the end of my expression but stil the same : com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList@1d8957f
-
Sembrano over 10 yearsmaybe I should just try dom parsing instead for my project seems more easy. In the end I need to be able to save the new edited sml data anyway.
-
Ian Roberts over 10 years@Sembrano XPath or DOM on its own can't give you a
String[]
, but it can give you aNodeList
which is essentially like an array of nodes - for each item in the node list,.getLocalName()
will give you the element name (DESCRIPTION
,PARENT
, etc.) and.getTextContent()
will give you the corresponding value ("Site parameters"
,"NULL"
, etc.) -
Sembrano over 10 yearsIve tried your solution but nothing is being written in the console.
-
Sembrano over 10 yearshm, I do have a <root> and <info> aswell in the xml file but cant post the <info> and root since its company stuffs in there. so I njust noticed that <type> seem to be a child to root. since the code aint working I mean
-
Ian Roberts over 10 years@Sembrano that's what I meant by "assuming the XML you gave in the question is a true representation of the real structure" - if it isn't then you'll obviously have to adjust the XPath expression to match, there are many XPath tutorials on the web that can help you work out the right expression for your specific case.
-
Nick Holt over 10 yearsHow are you creating the
document
. I created aString
from the XML above, turned it into abyte[]
and calledDocument document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(xmlBytes))
. -
Sembrano over 10 yearsYes but if I do //Object_type then it shouldnt matter since then it get the object_type no matter where its located.
-
Sembrano over 10 yearsIm just parsing my xmlfile in my document : Document xmlDocument = builder.parse(file);