0

I am trying to figure out how to go about getting the value of jxdm:ID from the following XML file:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?> 
<My:Message 
xmlns:Abcd="http://...." 
xmlns:box-1="http://...." 
xmlns:bulb="http://...."
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xsi:schemaLocation="http://....stores.xsd">
<Abcd:StoreDataSection>
    <Abcd:DataSection>
        <Abcd:FirstStore>
            <box-1:Response>
                <box-1:DataSection>
                    <box-1:Release>
                        <box-1:Activity>
                            <bulb:Date>2017-04-29</bulb:Date>
                            <bulb:Store xsi:type="TPIR:Organization">
                                <bulb:StoreID>
                                    <bulb:ID>D79G2102</bulb:ID>
                                </bulb:StoreID>
                            </bulb:Store>
                        </box-1:Activity>
                    </box-1:Release>
                </box-1:DataSection>
            </box-1:Response>
        </Abcd:FirstStore>
    </Abcd:DataSection>
</Abcd:StoreDataSection>
</ My:Message>

I keep getting "null" as the value of node

Node node = (Node) xPath.evaluate(expression, document, XPathConstants.NODE);

This is my current Java code:

try {
    DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = builderFactory.newDocumentBuilder();
    Document document = builder.parse(new File("c:/temp/testingNamespace.xml"));

    XPath xPath = XPathFactory.newInstance().newXPath();
    String expression = "//My/Message//Abcd/StoreDataSection/DataSection/FirstStore//box-1/Response/DataSection/Release/Activity//bulb/Store/StoreID/ID";
    Node node = (Node) xPath.evaluate(expression, document, XPathConstants.NODE);

    node.setTextContent("changed ID");

    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    transformer.transform(new DOMSource(document), new StreamResult(new File("C:/temp/test-updated.xml")));
} catch (Exception e) {
    System.out.println(e.getMessage());
}

How would the correct XPath be formatted in order for me to get that value and change it?

Update 1

So something like this?

String expression = "/My:Message/Abcd:StoreDataSection/Abcd:DataSection/Abcd:FirstStore/box-1:Response/box-1:DataSection/box-1:Release/box-1:Activity/bulb:Store/bulb:StoreID/bulb:ID";
StealthRT
  • 10,108
  • 40
  • 183
  • 342
  • @kjhughes understood. Thanks. – StealthRT Dec 16 '19 at 16:23
  • Your updated XML still doesn't include a declaration for the `My` namespace prefix. The XPath looks syntactically correct now at least, though. – kjhughes Dec 16 '19 at 16:49
  • @kjhughes I assure you the xml file i am using is correctly formatted and all. The original just has sensitive data and names so I needed to change them in order to be able to post i - I just need help on getting the correct path to the value I need. – StealthRT Dec 16 '19 at 17:40
  • Then the XML is still not namespace-well-formed due the use of the undefined namespace prefix, `My`. A useful sanity check in such circumstances is to first make sure that your code works with well-formed XML and a simple XPath such as just selecting the root element. Next progressively add XPath steps. Next add in the undeclared namespace prefix (which is the elephant in the room right now). – kjhughes Dec 16 '19 at 17:46
  • Also, note that *correctly formatted*, as you say, is not the same as ***well-formed***, which has a very precise technical meaning. Be sure to understand that to be [***well-formed***](https://stackoverflow.com/a/25830482/290085) is to meet the requirements for being XML. To fail to be well-formed is to fail to meet the requirements of conformant XML libraries and tools. – kjhughes Dec 17 '19 at 15:51

2 Answers2

0

Two things wrong here:

  1. Your XML is not namespace-well-formed; it does not declare the used namespace prefixes.
  2. Once namespace prefixes are properly declared in the XML and in your Java code, you use them in XPath via : not via /. So, it'd be not /Abcd/StoreDataSection but rather /Abcd:StoreDataSection (and so on for the rest of the steps in your XPath).

See also How does XPath deal with XML namespaces?


I am unable to change anything in the XML so I have to go with it as-is sadly.

Technically you might be able to use some XML tools with undeclared namespaces because this omission only renders the XML only namespace-not-well-formed. Many tools expect not only well-formed but also namespace-well-formed XML. (See Namespace-Well-Formed for the difference)

Otherwise, see How to parse invalid (bad / not well-formed) XML? to repair your XML.

kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • Thanks for the reply, kjhughes. However, I am unable to change anything in the XML so I have to go with it as-is sadly. – StealthRT Dec 16 '19 at 16:05
0

The problem is that you should access to Node by prefix (if you want to) but in a different way, like: //bulb:StoreID if you want to access StorID for example.

Then again it would still not work because you need to tell XPath how to resolve namspaces prefixes.

You should check this answer : How to query XML using namespaces in Java with XPath? for details on how to implement and use a NamespaceContext.

The bottom line is that you need to implement a javax.xml.namespace.NamespaceContext and set it to the XPath.

        XPath xpath = XPathFactory.newInstance().newXPath();
        NamespaceContext context = new MyNamespaceContext();

        xpath.setNamespaceContext(context);
minus
  • 2,646
  • 15
  • 18