使用 XPATH 和 Java 中的 DOM 更新空 XML 元素

Update empty XML element using XPATH and DOM in Java

我将以下 XML 文档存储为字符串,并希望使用 XPATH 和 DOM.

更新空的 SubID 元素
<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>

我使用以下代码首先使用 xpath 获取节点并使用 DOM 更新它。由于 XML 文档使用命名空间,我在 xpath 中使用 local-name()

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();
}

在目标节点中设置值后,我尝试将其转换回字符串并打印出来进行验证,但我看到 SubId 字段已折叠且未添加任何内容。这是我得到的输出(仅发布 XML 输出的相关部分):

<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>

我做错了什么?

我发现如果元素为空,使用 setNodeValue 将不起作用,我们可以创建一个文本节点,然后将其作为子节点追加到目标节点中。

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);
}

参考:Adding a Text Node to a DOM Document