Unable to read xml with namespace prefix using DOM

2019-01-26 09:50发布

问题:

This is the input XML:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns2:SendResponse xmlns:ns2="http://mycompany.com/schema/">
         <ns2:SendResult>
            <ns2:Token>A00179-02</ns2:Token>
         </ns2:SendResult>
      </ns2:SendResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This the code that I'm using to read the XML (Variable xmlString contains the XML above):

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xmlString));
Document doc = db.parse(is);

System.out.println("Element :" + doc.getElementsByTagName("Token").item(0));
System.out.println("Element :" + doc.getElementsByTagName("ns2:Token").item(0));

Output:

Element :null
Element :[ns2:Token: null]

I'm able to read the element if I use "ns2:Token" as the tag name, but I don't want to use the prefix in my code as I'm not sure if it'll be the same or change in the future. Is there any way to read the xml element without hard-coding the namespace in the tag name?

回答1:

The W3C dom method for namespaced elements:

getElementsByTagNameNS

NodeList getElementsByTagNameNS(String namespaceURI,
                                String localName)

    Returns a NodeList of all the Elements with a given local name and namespace URI in document order.

    Parameters:
        namespaceURI - The namespace URI of the elements to match on. The special value "*" matches all namespaces.
        localName - The local name of the elements to match on. The special value "*" matches all local names. 
    Returns:
        A new NodeList object containing all the matched Elements.
    Since:
        DOM Level 2

IIRC earlier version of the W3C DOM had poor support for namespaces so I don't use it. However if you use the above with the full namespaceURI http://schemas.xmlsoap.org/soap/envelope/ it should work. The prefix is unimportant - it has no permanency outside the document it is used in.

so try:

System.out.println("Element :" + doc.getElementsByTagNameNS(
        "http://schemas.xmlsoap.org/soap/envelope/", "Token").item(0));


回答2:

get the namespace first

docFactory.setNamespaceAware(true);
StringBuilder nameSpace = new StringBuilder(
                    doc.getDocumentElement().getPrefix() != null ? doc.getDocumentElement().getPrefix() + ":" : "");

then use nameSpace variable accrodingly

eg:

Node node= doc.getElementsByTagName(nameSpace + "Node1").item(0)
                    .getFirstChild();


回答3:

You could always assign the namespace to a variable, this would allow changing it on the fly in the future.



回答4:

Try to use XPath expression. See sample code below.

Document doc = dBuilder.parse(new ByteArrayInputStream(responseXML.getBytes()));
doc.getDocumentElement().normalize();
XPath xPath =  XPathFactory.newInstance().newXPath();

String expression = "/ns6:ReadPersonReturn/ns6:object/ns3:Person/ns3:Phone/ns3:item";
NodeList nodes = (NodeList) xPath.compile(expression).evaluate(doc, XPathConstants.NODESET);
Element secondNode = null;
if(nodes != null && nodes.getLength() > 0){
    secondNode = (Element) leadCloudPingRecordNodes.item(i);
}