2

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();
        }
    }
Sembrano
  • 1,127
  • 4
  • 18
  • 28

2 Answers2

3

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);
Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
  • Yes 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/*"; – Sembrano Jan 21 '14 at 14:14
  • 1
    @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. – Ian Roberts Jan 21 '14 at 14:16
  • Is 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? – Nick Holt Jan 21 '14 at 14:20
  • 1
    @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. – Ian Roberts Jan 21 '14 at 14:26
  • I see, interesting, though the question does mention selecting from a list, rather than the user typing the 'type' explicitly. – Nick Holt Jan 21 '14 at 14:32
  • This code prints out null : dataProps : null siteProps : null objectProps : null – Sembrano Jan 21 '14 at 14:37
  • 1
    @Sembrano in order to use XPath you have to parse your document with a namespace-aware parser - call `builderFactory.setNamespaceAware(true);` before you do `builderFactory.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 Jan 21 '14 at 14:45
  • 1
    @Sembrano the code you posted does not call `setNamespaceAware(true)` – Ian Roberts Jan 21 '14 at 14:55
  • I 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 – Sembrano Jan 21 '14 at 15:08
  • @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 the `props` instead, either way you'll need to use DOM APIs from this point on. – Ian Roberts Jan 21 '14 at 15:11
  • yeh 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 Jan 21 '14 at 15:25
  • maybe 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. – Sembrano Jan 21 '14 at 15:30
  • 1
    @Sembrano XPath or DOM on its own can't give you a `String[]`, but it can give you a `NodeList` 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.) – Ian Roberts Jan 21 '14 at 15:34
  • hm, I do have a and aswell in the xml file but cant post the and root since its company stuffs in there. so I njust noticed that seem to be a child to root. since the code aint working I mean – Sembrano Jan 21 '14 at 15:47
  • 2
    @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. – Ian Roberts Jan 21 '14 at 15:57
  • Yes but if I do //Object_type then it shouldnt matter since then it get the object_type no matter where its located. – Sembrano Jan 22 '14 at 07:00
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
Nick Holt
  • 33,455
  • 4
  • 52
  • 58
  • What's with the downvote - that XPath works and solves the question. – Nick Holt Jan 21 '14 at 14:08
  • Yes 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 Jan 21 '14 at 14:14
  • That won't work, the string concatenation in the example in my answer will where `getObjType()` returns `SiteData` 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 Jan 21 '14 at 14:19
  • Ive tried your solution but nothing is being written in the console. – Sembrano Jan 21 '14 at 15:40
  • How are you creating the `document`. I created a `String` from the XML above, turned it into a `byte[]` and called `Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(xmlBytes))`. – Nick Holt Jan 21 '14 at 15:59
  • Im just parsing my xmlfile in my document : Document xmlDocument = builder.parse(file); – Sembrano Jan 22 '14 at 07:01