I have the following XML document stored as a String and want to update the SubID
element that is empty using XPATH and DOM.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://example.com/2.0/" xmlns:pfx="http://example.com/1.0"> <soapenv:Header/> <soapenv:Body> <soapenv:Fault> <faultcode>soapenv:Client</faultcode> <faultstring>Authentication Failed</faultstring> <detail> <pfx:Fault> <ns2:MessageHeader xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns1="example.com/1.0/" xmlns:ns2="http://www.example.com/MessageHeader/1.0/"> <ns2:SubId></ns2:SubId> <ns2:CnsmrId></ns2:CnsmrId> </ns2:MessageHeader> </pfx:Fault> </detail> </soapenv:Fault> </soapenv:Body> </soapenv:Envelope>
I’m using the following code to fetch the node first using xpath and update it using DOM. As the XML document use namespaces I’m using local-name()
in the xpath.
public void updateXML(String xmlString, String textToAdd) { Document responseDoc = convertStringToXMLDocument(xmlString.trim()); responseDoc.getDocumentElement().normalize(); XPath xpath = XPathFactory.newInstance().newXPath(); String destinationPath = "//*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='Fault']/*[local-name()='detail']/*[local-name()='Fault']/*[local-name()='MessageHeader']/*[local-name()='SubId']"; Node destinationNode = (Node) xpath.compile(destinationPath).evaluate(responseDoc, XPathConstants.NODE); destinationNode.setNodeValue(textToAdd); String respDoc = convertDOMtoXMLString(responseDoc); System.out.println(respDoc); } private static Document convertStringToXMLDocument(String xmlString) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = null; factory.setNamespaceAware(true); builder = factory.newDocumentBuilder(); Document doc = builder.parse(new InputSource(new StringReader(xmlString))); return doc; } private static String convertDOMtoXMLString(Document doc) { DOMSource domSource = new DOMSource(doc); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer; transformer = tf.newTransformer(); transformer.transform(domSource, result); return writer.toString(); }
After setting the value in the destination node I tried to convert it back to string and print it to verify but I see that the SubId
field is collapsed and nothing has been added to it. Here’s the output I got (Posting only relevant part of XML output):
<pfx:Fault> <ns2:MessageHeader xmlns:ns1="qwerty.com/1.0/" xmlns:ns2="http://www.abcd.com/MessageHeader/1.0/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <ns2:SubId /> <ns2:CnsmrId /> </ns2:MessageHeader> </pfx:Fault>
What am I doing wrong?
Advertisement
Answer
I fgured out using setNodeValue will not work if the element is empty, we can create a text node and then append it as a child node in the detination node.
public void updateXML(String xmlString, String textToAdd) { Document responseDoc = convertStringToXMLDocument(xmlString.trim()); responseDoc.getDocumentElement().normalize(); XPath xpath = XPathFactory.newInstance().newXPath(); String destinationPath = "//*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='Fault']/*[local-name()='detail']/*[local-name()='Fault']/*[local-name()='MessageHeader']/*[local-name()='SubId']"; Node destinationNode = (Node) xpath.compile(destinationPath).evaluate(responseDoc, XPathConstants.NODE); Text txt = responseDoc.createTextNode("Hello"); destinationNode.appendChild(txt); String respDoc = convertDOMtoXMLString(responseDoc); System.out.println(respDoc); }