简体   繁体   中英

XPath select element with an attribute

I have a xml file which looks like:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
 <alarm-response-list xmlns="http://www.ca.com/spectrum/restful/schema/response"   
 error="EndOfResults" throttle="277" total-alarms="288">
 <alarm-responses> 
  <alarm id="53689bf8-6cc8-1003-0060-008010186429">
   <attribute id="0x11f4a" error="NoSuchAttribute" /> 
   <attribute id="0x12b4c">UPS DIAGNOSTIC TEST FAILED</attribute> 
   <attribute id="0x10b5a">IDG860237, SL3-PL4, US, SapNr=70195637,</attribute> 
 </alarm>
 <alarm id="536b8c9a-28b3-1008-0060-008010186429">
  <attribute id="0x11f4a" error="NoSuchAttribute" /> 
  <attribute id="0x12b4c">DEVICE IN MAINTENANCE MODE</attribute> 
  <attribute id="0x10b5a">IDG860237, SL3-PL4, US, SapNr=70195637,</attribute> 
</alarm>
</alarm-responses>
</alarm-response-list>

There a lot of these alarms. Now I want save for every alarm tag the attribute with the id = 0x10b5a in a String. But I haven't a great clue. In my way it doesn't do it. I get only showed the expression.

My idea:

FileInputStream file = new FileInputStream(
                new File(
                        "alarms.xml"));

        DocumentBuilderFactory builderFactory = DocumentBuilderFactory
                .newInstance();

        DocumentBuilder builder = builderFactory.newDocumentBuilder();

        Document xmlDocument = builder.parse(file);

        XPath xPath = XPathFactory.newInstance().newXPath();

        System.out.println("*************************");
        String expression = "/alarm-responses/alarm/attribute[@id='0x10b5a'] ";
        System.out.println(expression);
        NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(
                xmlDocument, XPathConstants.NODESET);
        for (int i = 0; i < nodeList.getLength(); i++) {
            System.out.println(nodeList.item(i).getFirstChild()
                    .getNodeValue());
        }

There are several different problems here that are interacting to mean that your XPath expression doesn't match anything. Firstly the alarm-responses element isn't the root of the document - you need an extra step on the front of the path to select the alarm-response-list element. But more importantly you have namespace issues.

XPath only works when the XML has been parsed with namespaces enabled, which for some reason is not the default for DocumentBuilderFactory . You need to enable namespaces before you do newDocumentBuilder .

Now your XML document has xmlns="http://www.ca.com/spectrum/restful/schema/response" , which puts all the elements in this namespace, but unprefixed node names in an XPath expression always refer to nodes that are not in a namespace. In order to match namespaced nodes you need to bind a prefix to the namespace URI and then use prefixed names in the path.

For javax.xml.xpath this is done using a NamespaceContext , but annoyingly there is no default implementation of this interface available by default in the Java core library. There is a SimpleNamespaceContext implementation available as part of Spring, or it's fairly simple to write your own. Using the Spring class:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory
        .newInstance();

// enable namespaces
builderFactory.setNamespaceAware(true);

DocumentBuilder builder = builderFactory.newDocumentBuilder();

Document xmlDocument = builder.parse(file);

XPath xPath = XPathFactory.newInstance().newXPath();

// Set up the namespace context
SimpleNamespaceContext ctx = new SimpleNamespaceContext();
ctx.bindNamespaceUri("ca", "http://www.ca.com/spectrum/restful/schema/response");
xPath.setNamespaceContext(ctx);

System.out.println("*************************");

// corrected expression
String expression = "/ca:alarm-response-list/ca:alarm-responses/ca:alarm/ca:attribute[@id='0x10b5a']";

System.out.println(expression);
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(
        xmlDocument, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
    System.out.println(nodeList.item(i).getTextContent());
}

Note also how I'm using getTextContent() to get the text under each matched element. The getNodeValue() method always returns null for element nodes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM