【问题标题】:Attempting to use Java & Xpath to updated the value of any selected node inside an XML doc尝试使用 Java 和 Xpath 更新 XML 文档中任何选定节点的值
【发布时间】:2021-04-30 18:10:29
【问题描述】:

我开发了 GUI 工具,将 XML 文档显示为可编辑的 JTree,用户可以在 JTree 中选择一个节点并尝试更改 XML 文档中的实际节点值。

我遇到的问题是构建尝试实际更新的正确 Xpath 查询。

这里是 JTree 的 GUI,显示了哪个元素被选中并应该被编辑:

它是一个非常大的 XMl,所以这里是 XML 的折叠 sn-p:

更新(忽略尝试 1 和 2,第一个问题已解决,转到尝试 3 和 4)

尝试 1 #(尝试创建 XPath 查询以更新节点值的相关 Java 方法):

public void updateXmlData(JTree jTree, org.w3c.dom.Document doc, TreeNode parentNode, String oldValue, String newValue) throws XPathExpressionException {
        System.out.println("Selected path=" + jTree.getSelectionPath().toString());

        String[] pathTockens = jTree.getSelectionPath().toString().split(",");
        StringBuilder sb = new StringBuilder();
        //for loop to construct xpath query 
        for (int i = 0; i < pathTockens.length - 1; i++) {
            if (i == 0) {
                sb.append("//");
            } else {
                sb.append(pathTockens[i].trim());
                sb.append("/");
            }

        }//end for loop 
        sb.append("text()");
        System.out.println("Constructed XPath Query:" + sb.toString());
        //new xpath 
        XPath xpath = XPathFactory.newInstance().newXPath();
        //compile query 
        NodeList nodes = (NodeList) xpath.compile(sb.toString()).evaluate(doc, XPathConstants.NODESET);
        //Make the change on the selected nodes
        for (int idx = 0; idx < nodes.getLength(); idx++) {
            Node value = nodes.item(idx).getAttributes().getNamedItem("value");
            String val = value.getNodeValue();
            value.setNodeValue(val.replaceAll(oldValue, newValue));
        }
        //set the new updated xml doc 
        SingleTask.currentTask.setDoc(doc);
    }

控制台日志:

Selected path=[C:\Users\xyz\Documents\XsdToXmlFiles\sampleIngest.xml, Ingest, Property_Maps, identifier, identifieXYZ]
Constructed XPath Query://Ingest/Property_Maps/identifier/text()
Jan 26, 2021 2:04:16 PM com.xyz.XmlToXsdValidator.Views.EditXmlTreeNodeDialogJFrame jButtonOkEditActionPerformed
SEVERE: null

javax.xml.transform.TransformerException: Unable to evaluate expression using this context
    at com.sun.org.apache.xpath.internal.XPath.execute(XPath.java:368)

正如您在日志中看到的:

选定路径=[C:\Users\xyz\Documents\XsdToXmlFiles\sampleIngest.xml, Ingest, Property_Maps, identifier, identifieXYZ]

构造的 XPath 查询://Ingest/Property_Maps/identifier/text()

路径是正确的,基本上是Ingest->Property_Maps->identifier->text()

但我得到了:

 javax.xml.transform.TransformerException: Unable to evaluate expression using this context

尝试 2 #(尝试创建 XPath 查询以更新节点值的相关 Java 方法):

public void updateXmlData(JTree jTree, org.w3c.dom.Document doc, TreeNode parentNode, String oldValue, String newValue) throws XPathExpressionException {
        // Locate the node(s) with xpath
        System.out.println("Selected path=" + jTree.getSelectionPath().toString());

        String[] pathTockens = jTree.getSelectionPath().toString().split(",");
        StringBuilder sb = new StringBuilder();
        //loop to construct xpath query 
        for (int i = 0; i < pathTockens.length - 1; i++) {
            if (i == 0) {
                sb.append("//");
            } else {
                sb.append(pathTockens[i].trim());
                sb.append("/");
            }
        }//end loop 

        sb.append("[text()=");
        sb.append("'");
        sb.append(oldValue);
        sb.append("']");
        int lastIndexOfPathChar = sb.lastIndexOf("/");
        sb.replace(lastIndexOfPathChar, lastIndexOfPathChar + 1, "");
        System.out.println("Constructed XPath Query:" + sb.toString());
        //new xpath instance 
        XPath xpath = XPathFactory.newInstance().newXPath();
        NodeList nodes = (NodeList) xpath.evaluate(sb.toString(), doc, XPathConstants.NODESET);

        //Make the change on the selected nodes
        for (int idx = 0; idx < nodes.getLength(); idx++) {
            Node value = nodes.item(idx).getAttributes().getNamedItem("value");
            String val = value.getNodeValue();
            value.setNodeValue(val.replaceAll(oldValue, newValue));
        }

        SingleTask.currentTask.setDoc(doc);
    }

我能够解决基于异常的 Andreas 注释,并且没有更多异常/错误,但是 XPath 查询没有找到选定的节点。返回空

新更新的代码:

尝试 #3 使用自定义命名空间解析器。参考:https://www.kdgregory.com/index.php?page=xml.xpath

   public boolean updateXmlData(JTree jTree, org.w3c.dom.Document doc, TreeNode parentNode, String oldValue, String newValue) throws XPathExpressionException {
            System.out.println("Selected path=" + jTree.getSelectionPath().toString());
            boolean changed = false;
            // Locate the node(s) with xpath
            String[] pathTockens = jTree.getSelectionPath().toString().split(",");
            StringBuilder sb = new StringBuilder();
            //loop to construct xpath query 
            for (int i = 0; i < pathTockens.length - 1; i++) {
    
                if (i == 0) {
                    //do nothing
                } else if (i == 1) {
                    sb.append("/ns:" + pathTockens[i].trim());
                } else if (i > 1 && i != pathTockens.length - 1) {
                    sb.append("/ns:" + pathTockens[i].trim());
                } else {
                    //sb.append("/" + pathTockens[i].trim());
                }
            }//end loop 
    
            sb.append("[text()=");
            sb.append("'");
            sb.append(oldValue);
            sb.append("']");
    
            System.out.println("Constructed XPath Query:" + sb.toString());
            //new xpath instance 
            XPathFactory xpathFactory = XPathFactory.newInstance(); 
            XPath xpath = xpathFactory.newXPath();
          
            xpath.setNamespaceContext(new UniversalNamespaceResolver(SingleTask.currentTask.getXsdFile().getXsdNameSpace()));
            NodeList nodes = (NodeList) xpath.evaluate(sb.toString(), doc, XPathConstants.NODESET);
    
            //start for 
            Node node;
            String val = null;
            for (int idx = 0; idx < nodes.getLength(); idx++) {
                if (nodes.item(idx).getAttributes() != null) {
    
                    node = nodes.item(idx).getAttributes().getNamedItem("value");
                    if (node != null) {
    
                        val = node.getNodeValue();
                        node.setNodeValue(val.replaceAll(oldValue, newValue));
                        changed = true;
                        break;
                    }//end if node is found
                }
            }//end for 
            //set the new updated xml doc 
            SingleTask.currentTask.setDoc(doc);
    
            return changed;
        }

实现自定义命名空间解析器的类:

 import java.util.Arrays;
    import java.util.Collections;
    import java.util.Iterator;
    import java.util.List;
    import javax.xml.XMLConstants;
    import javax.xml.namespace.NamespaceContext;
    import org.w3c.dom.Document;
    
        /**
         *
         * References:https://www.kdgregory.com/index.php?page=xml.xpath
         */
        //custom  NamespaceContext clss implementation 
        public class UniversalNamespaceResolver implements NamespaceContext
        {
            private String _prefix = "ns";
            private String _namespaceUri=null; 
            private List<String> _prefixes = Arrays.asList(_prefix);
            
            public UniversalNamespaceResolver(String namespaceResolver)
            {
                _namespaceUri = namespaceResolver; 
            }
         
            @Override
            @SuppressWarnings("rawtypes")
            public Iterator getPrefixes(String uri)
            {
                if (uri == null)
                    throw new IllegalArgumentException("UniversalNamespaceResolver getPrefixes() URI may not be null");
                else if (_namespaceUri.equals(uri))
                    return _prefixes.iterator();
                else if (XMLConstants.XML_NS_URI.equals(uri))
                    return Arrays.asList(XMLConstants.XML_NS_PREFIX).iterator();
                else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri))
                    return Arrays.asList(XMLConstants.XMLNS_ATTRIBUTE).iterator();
                else
                    return Collections.emptyList().iterator();
            }
        
        
            @Override
            public String getPrefix(String uri)
            {
                if (uri == null)
                    throw new IllegalArgumentException("nsURI may not be null");
                else if (_namespaceUri.equals(uri))
                    return _prefix;
                else if (XMLConstants.XML_NS_URI.equals(uri))
                    return XMLConstants.XML_NS_PREFIX;
                else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri))
                    return XMLConstants.XMLNS_ATTRIBUTE;
                else
                    return null;
            }
        
        
            @Override
            public String getNamespaceURI(String prefix)
            {
                if (prefix == null)
                    throw new IllegalArgumentException("prefix may not be null");
                else if (_prefix.equals(prefix))
                    return _namespaceUri;
                else if (XMLConstants.XML_NS_PREFIX.equals(prefix))
                    return XMLConstants.XML_NS_URI;
                else if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix))
                    return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
                else
                    return null;
            }
        }

控制台输出:

所选路径=[C:\Users\xyz\DocumentsIngest_LDD.xml, Ingest_LDD, Property_Maps, identifier, identifier1]

构造的 XPath: 查询:/ns:Ingest_LDD/ns:Property_Maps/ns:identifier[text()='identifier1']

尝试 #4(没有自定义命名空间解析器):

public boolean updateXmlData(JTree jTree, org.w3c.dom.Document doc, TreeNode parentNode, String oldValue, String newValue) throws XPathExpressionException {
        System.out.println("Selected path=" + jTree.getSelectionPath().toString());
        boolean changed = false;
        // Locate the node(s) with xpath
        String[] pathTockens = jTree.getSelectionPath().toString().split(",");
        StringBuilder sb = new StringBuilder();
        //loop to construct xpath query 
        for (int i = 0; i < pathTockens.length - 1; i++) {

            if (i == 0) {
                //do nothing
            } else if (i == 1) {
                sb.append("/" + pathTockens[i].trim());
            } else if (i > 1 && i != pathTockens.length - 1) {
                sb.append("/" + pathTockens[i].trim());
            } else {
                //sb.append("/" + pathTockens[i].trim());
            }
        }//end loop 

        sb.append("[text()=");
        sb.append("'");
        sb.append(oldValue);
        sb.append("']");

        System.out.println("Constructed XPath Query:" + sb.toString());
        //new xpath instance 
        XPathFactory xpathFactory = XPathFactory.newInstance(); 
        XPath xpath = xpathFactory.newXPath();
      
        //WITHOUT CUSTOM NAMESPACE CONTEXT xpath.setNamespaceContext(new UniversalNamespaceResolver(SingleTask.currentTask.getXsdFile().getXsdNameSpace()));
        NodeList nodes = (NodeList) xpath.evaluate(sb.toString(), doc, XPathConstants.NODESET);

        //start for 
        Node node;
        String val = null;
        for (int idx = 0; idx < nodes.getLength(); idx++) {
            if (nodes.item(idx).getAttributes() != null) {

                node = nodes.item(idx).getAttributes().getNamedItem("value");
                if (node != null) {

                    val = node.getNodeValue();
                    node.setNodeValue(val.replaceAll(oldValue, newValue));
                    changed = true;
                    break;
                }//end if node is found
            }
        }//end for 
        //set the new updated xml doc 
        SingleTask.currentTask.setDoc(doc);

        return changed;
    }

控制台输出:

选定路径=[C:\Users\anaim\Documents\XsdToXmlFiles\sampleIngest_LDD.xml, Ingest_LDD, Property_Maps, identifier, identifier1]

构造的 XPath 查询:/Ingest_LDD/Property_Maps/identifier[text()='identifier1']

我实际上使用 (https://www.freeformatter.com/xpath-tester.html#ad-output) 在线手动编写了 XPath 查询

抱歉,我无法提供示例 XMl,它太大了。

手动 XPath 查询是:

/Ingest_LDD/Property_Maps/identifier[text()='identifier1']

在线工具成功找到文本并输出:

Element='<identifier xmlns="http://pds.nasa.gov/pds4/pds/v1">identifier1</identifier>' 

因此,我在尝试 #4 下的代码 & 查询应该可以工作?

用户输入后的更新尝试:

尝试 #5(基于用户的响应,命名空间感知 = TRUE ),相关代码如下

factory.setNamespaceAware(true);
doc = dBuilder.parse(xmlFile);

if (doc!=null)
{
    //***NOTE program comes meaning doc is NOT null,  however inspecting it shows [#document: null]

    doc.getDocumentElement().normalize();
}


xpath.setNamespaceContext(new UniversalNamespaceResolver(SingleTask.currentTask.getXsdFile().getXsdNameSpace()));
Node node = (Node) xpath.evaluate(sb.toString(), doc, XPathConstants.NODE);
if (node!=null)
{
    // See https://docs.oracle.com/javase/9/docs/api/org/w3c/dom/Node.html#setTextContent-java.lang.String-
    node.setTextContent(newValue);
    SingleTask.currentTask.setDoc(doc);
}

输出(再次找不到节点):

Selected path=[C:\Users\xyz\Documents\XsdToXmlFiles\sampleIngest_LDD.xml, Ingest_LDD, name, name1]
Constructed XPath Query:/Ingest_LDD/name[text()='name1']
Error changing value!

尝试 #6(基于用户的响应,命名空间感知 = FALSE)

factory.setNamespaceAware(false);
doc = dBuilder.parse(xmlFile);

if (doc!=null)
{
    //***NOTE program comes meaning doc is NOT null,  however inspecting it shows [#document: null]
    doc.getDocumentElement().normalize();
}


//COMMENTED OUT , SINCE NAMESPACE AWARE FALSE xpath.setNamespaceContext(new UniversalNamespaceResolver(SingleTask.currentTask.getXsdFile().getXsdNameSpace()));
Node node = (Node) xpath.evaluate(sb.toString(), doc, XPathConstants.NODE);
if (node!=null)
{
    // See https://docs.oracle.com/javase/9/docs/api/org/w3c/dom/Node.html#setTextContent-java.lang.String-
    node.setTextContent(newValue);
    SingleTask.currentTask.setDoc(doc);
}

输出(再次找不到节点):

Selected path=[C:\Users\xyz\Documents\XsdToXmlFiles\sampleIngest_LDD.xml, Ingest_LDD, name, name1]
Constructed XPath Query:/Ingest_LDD/name[text()='name1']
Error changing value!

根据(DocumentBuilder.parse(InputStream) returns null),返回为[#document: null] 的文档实际上可能不是问题???

尝试 #7(命名空间感知 FALSE)

同样NamedNodeMap namedNodeMap = doc.getAttributes(); 返回 NULL。

但是,Node firstChild = doc.getFirstChild() 实际上返回的是有效元素!

我将 firstChild 传递给 xpath.evaluate(sb.toString(), firstChild , XPathConstants.NODE);,但再次找不到所需的节点。

输出(再次找不到节点):

Selected path=[C:\Users\xyz\Documents\XsdToXmlFiles\sampleIngest_LDD.xml, Ingest_LDD, name, name1]
Constructed XPath Query:/Ingest_LDD/name[text()='name1']
Error changing value!

尝试 #8(命名空间感知错误)

我还尝试将doc.getChildNodes() 传递给xpath.evaluate() 而不是doc 对象作为最后的绝望尝试,请参见下面的sn-p。

if (doc != null) {

            NodeList nodes = (NodeList) xpath.evaluate(sb.toString(), doc.getChildNodes(), XPathConstants.NODESET);

            String val = null;
            Node node; 
            for (int idx = 0; idx < nodes.getLength(); idx++) {
                if (nodes.item(idx).getAttributes() != null) {

                    node = nodes.item(idx).getAttributes().getNamedItem("value");
                    if (node != null) {

                        val = node.getNodeValue();
                        node.setNodeValue(val.replaceAll(oldValue, newValue));
                        changed = true;
                        break;
                    }//end if node is found
                }
            }//end for 
        }

输出(再次找不到节点):

Selected path=[C:\Users\xyz\Documents\XsdToXmlFiles\sampleIngest_LDD.xml, Ingest_LDD, name, name1]
Constructed XPath Query:/Ingest_LDD/name[text()='name1']
Error changing value!

【问题讨论】:

  • 这能回答你的问题吗? Unable to evaluate expression in XPath
  • 首先,您尝试映射两个不同的数据模型。因此,您需要明确规定规则。例如:你如何表示属性或命名空间?其次,有很多动态构建 XPath 表达式的例子。对于 Shure,Java 语言有一个。如果您打算只使用 in situ 元素节点(而不是假装在另一个应用程序中使用这些节点),则需要以以下形式堆叠位置步骤:/*[ + preceding 兄弟元素加一 + ]。在你的情况下:/*[1]/*[16]/*[1]

标签: java xml xpath


【解决方案1】:

对于您在线执行的测试,您的 XML 文件似乎包含命名空间信息。

考虑到这些信息,您的两个 XPath 评估示例可能会起作用,也可能不起作用,这取决于几件事。

例如,您可能可以使用尝试 #4,如果您使用的是非命名空间感知(默认)DocumentBuilderFactory 并且您没有在 XPath 表达式中提供任何命名空间信息,那么 XPath 评估就足够了.

但是,如果应用相反的条件,尝试 #3 中的 XPath 评估也可能是足够的,即,您使用的是命名空间感知 DocumentBuilderFactory

DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
f.setNamespaceAware(true);

并且您在 XPath 表达式中提供命名空间信息和方便的NamespaceContext 实现。请参阅 this 相关的 SO 问题和 this 很棒的 IBM 文章。

请注意,您不需要在 XML 文件和 XPath 表达式中提供相同的名称空间前缀,唯一的要求是 XML 中的名称空间感知(XPath 始终是名称空间感知的)。

鉴于这种情况,我认为您可以同时应用这两种方法。

无论如何,我认为问题可能与您处理实际文本替换的方式有关:您正在寻找具有value 属性的节点,并查看关联的XML Schema 此属性确实不存在。

请考虑以下方法:

// You can get here following both attempts #3 an #4
Node node = (Node) xpath.evaluate(sb.toString(), doc, XPathConstants.NODE);
boolean changed = node != null;
if (changed) {
  // See https://docs.oracle.com/javase/9/docs/api/org/w3c/dom/Node.html#setTextContent-java.lang.String-
  node.setTextContent(newValue);
  SingleTask.currentTask.setDoc(doc);
}

return changed;

此代码假定所选节点是唯一的才能正常工作。

虽然可能不同,但请注意,如果您为 XML 中的重复元素定义相同的值,则从 JTree 模型构造 XPath 选择器的方式可能会提供重复项。例如,考虑一下屏幕截图中的元素 external_id_property_maps

为了避免这种情况,您可以在构造 XPath 选择器时采用不同的方法。

不清楚您的代码 sn-p,但可能您使用 DefaultMutableTreeNode 作为基本 JTree 节点类型。如果是这种情况,您可以将所需的任意信息与每个节点相关联。

考虑例如创建一个简单的 POJO,它有两个字段,节点代表的 Element 的名称,以及某种唯一的、生成的 id,我们将其命名为 uiduuid 以避免与 id 属性混淆,很可能包含在原始 XML 文档中。

这个uid 应该与每个节点相关联。也许您可以利用 JTree 创建过程,在处理 XML 文件的每个节点时,也包括使用 UUID 类生成的此属性。

或者您可以在表示之前对原始 XML 文档应用 XSLT 转换:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:attribute name="uid">
        <xsl:value-of select="generate-id(.)"/>
      </xsl:attribute>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

进行此更改后,您的 XPath 查询应如下所示:

/ns:Ingest_LDD[@uid='w1ab1']/ns:Property_Maps[@uid='w1ab1a']/ns:identifier[@uid='w1ab1aq']

当然,有必要从JTree 的选定路径修改专门用于构造此表达式的代码,以将自定义对象考虑在内。

您可以将这种方法发挥到极致,并使用仅基于此 uid 属性的单个选择器,尽管我认为出于性能原因它不合适:

//*[@uid='w1ab1']

综合起来,你可以试试下面的方法。

请考虑这个 XML 文件:

<?xml version="1.0" encoding="utf-8" ?>
<Ingest_LDD xmlns="http://pds.nasa.gov/pds4/pds/v1"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://pds.nasa.gov/pds4/pds/v1 https://pds.nasa.gov/pds4/pds/v1/PDS4_PDS_1700.xsd">
    <!-- Please, forgive me, I am aware that the document is not XML Schema conformant,
    only for exemplification of the default namespace -->
    <Property_Maps>
        <identifier>identifier1</identifier>
    </Property_Maps>
</Ingest_LDD>

首先,我们来解析文档:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
// As the XML contains namespace, let's configure the parser namespace aware
// This will be of relevance when evaluating XPath
builderFactory.setNamespaceAware(true);
DocumentBuilder builder = builderFactory.newDocumentBuilder();
// Parse the document from some source
Document document = builder.parse(...);
// See http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work
document.getDocumentElement().normalize();

现在,创建一个与输入 XML 文件对应的 JTree 结构。首先,让我们创建一个方便的 POJO 来存储所需的树节点信息:

public class NodeInformation {
  // Node name
  private String name;
  // Node uid
  private String did;
  // Node value
  private String value;

  // Setters and getters

  // Will be reused by DefaultMutableTreeNode
  @Override
  public String toString() {
    return this.name;
  }
}

将 XML 文件转换为对应的 JTree

// Get a reference to root element
Element rootElement = document.getDocumentElement();
// Create root tree node
DefaultMutableTreeNode rootTreeNode = getNodeInformation(rootElement);
// Traverse DOM
traverse(rootTreeNode, rootElement);
// Create tree and tree model based on the computed root tree node
DefaultTreeModel treeModel = new DefaultTreeModel(rootTreeNode);
JTree tree = new JTree(treeModel);

地点:

private NodeInformation getNodeInformation(Node childElement) {
  NodeInformation nodeInformation = new NodeInformation();
  String name = childElement.getNodeName();
  nodeInformation.setName(name);
  // Provide a new unique identifier for every node
  String uid = UUID.randomUUID().toString();
  nodeInformation.setUid(uid);
  // Uhnn.... We need to associate the new uid with the DOM node as well. 
  // There is nothing wrong with it but mutating the DOM in this way in
  // a method that should be "read-only" is not the best solution.
  // It would be interesting to study the above-mentioned XSLT approach
  chilElement.setAttribute("uid", uid);

  // Compute node value
  StringBuffer buffer = new StringBuffer();
  NodeList childNodes = childElement.getChildNodes();
  boolean found = false;
  for (int i = 0; i < childNodes.getLength(); i++) {
    Node node = childNodes.item(i);
    if (node.getNodeType() == Node.TEXT_NODE) {
      String value = node.getNodeValue();
      buffer.append(value);
      found = true;
    }
  }

  if (found) {
    nodeInformation.setValue(buffer.toString());
  }
}

还有:

// Finds all the child elements and adds them to the parent node recursively
private void traverse(DefaultMutableTreeNode parentTreeNode, Node parentXMLElement) {
  NodeList childElements = parentXMLElement.getChildNodes();
  for(int i=0; i<childElements.getLength(); i++) {
    Node childElement = childElements.item(i);
    if (childElement.getNodeType() == Node.ELEMENT_NODE) {
      DefaultMutableTreeNode childTreeNode =
        new DefaultMutableTreeNode
          (getNodeInformation(childElement));
      parentTreeNode.add(childTreeNode);
      traverse(childTreeNode, childElement);
    }
  }
}

虽然您提供的NamespaceContext 实现看起来不错,但请首先尝试一些更简单的方法,以尽量减少出错的可能性。请参阅下面提供的实现。

那么,您的updateXMLData 方法应该如下所示:

public boolean updateXmlData(JTree tree, org.w3c.dom.Document doc, TreeNode parentNode, String oldValue, String newValue) throws XPathExpressionException {
  boolean changed = false;

  TreePath selectedPath = tree.getSelectionPath();
  int count = getPathCount();
  StringBuilder sb = new StringBuilder();
  NodeInformation lastNodeInformation;
  if (count > 0) {
    for (int i = 1; i < trp.getPathCount(); i++) {
      DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) trp.getPathComponent(i);
      NodeInformation nodeInformation = (NodeInformation) treeNode.getUserObject();
      sb.append(String.format("/ns:%s[@uid='%s']", nodeInformation.getName(), nodeInformation.getUid());
      lastNodeInformation = nodeInformation;
    }
  }

  System.out.println("Constructed XPath Query:" + sb.toString());

  // Although the `NamespaceContext` implementation you provided looks
  // fine, please, at a first step, try something simpler, to minimize the
  // possibility of error. For example:
  NamespaceContext nsContext = new NamespaceContext() {

    public String getNamespaceURI(String prefix) {
      if (prefix == null) {
        throw new IllegalArgumentException("No prefix provided!");
      } else if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
        return "http://pds.nasa.gov/pds4/pds/v1";
      } else if (prefix.equals("ns")) {
        return "http://pds.nasa.gov/pds4/pds/v1";
      } else {
        return XMLConstants.NULL_NS_URI;
      }
    }

    public String getPrefix(String namespaceURI) {
      // Not needed in this context.
      return null;
    }

    public Iterator getPrefixes(String namespaceURI) {
      // Not needed in this context.
      return null;
    }

  };

  //new xpath instance 
  XPathFactory xpathFactory = XPathFactory.newInstance(); 
  XPath xpath = xpathFactory.newXPath();
  // As the parser is namespace aware, we can safely use XPath namespaces
  xpath.setNamespaceContext(nsContext);
  
  Node node = (Node) xpath.evaluate(sb.toString(), doc, XPathConstants.NODE);
  boolean changed = node != null;
  if (changed) {
    // See https://docs.oracle.com/javase/9/docs/api/org/w3c/dom/Node.html#setTextContent-java.lang.String-
    node.setTextContent(newValue);
    SingleTask.currentTask.setDoc(doc);
    // Probably the information has been updated in the node, but just in case:
    lastNodeInformation.setValue(newValue);
  }

  return changed;
}

生成的 XPath 表达式如下所示:

/ns:Ingest_LDD[@uid='w1ab1']/ns:Property_Maps[@uid='w1ab1a']/ns:identifier[@uid='w1ab1aq']

如果你想使用默认命名空间,你也可以试试:

/:Ingest_LDD[@uid='w1ab1']/:Property_Maps[@uid='w1ab1a']/:identifier[@uid='w1ab1aq']

请注意,我没有测试过代码,但我希望你能明白。

只是为了澄清,为了给你一个正确的答案,如前所述,如果你现在删除或评论这行代码:

builderFactory.setNamespaceAware(true);

然后,XPath 表达式:

/ns:Ingest_LDD[@uid='w1ab1']/ns:Property_Maps[@uid='w1ab1a']/ns:identifier[@uid='w1ab1aq']

将不再找到所需的节点。现在,如果您从 XPath 表达式中删除命名空间信息:

/Ingest_LDD[@uid='w1ab1']/Property_Maps[@uid='w1ab1a']/identifier[@uid='w1ab1aq']

它会再次找到正确的节点。

【讨论】:

  • 我根据您的输入更新了我的尝试,但仅限于 XSLT 方法,请参阅更新。还是没有结果。我基于乐于助人接受了您的回答。还有更多见解吗?有解决办法吗??
  • 嗨@cyber101。非常感谢,我真的很感激。拜托,我真的想帮你,你有时间和我一起聊天吗?
  • 请不要担心[#document: null]的值,这并不意味着文档实际上是空的,它只是它的字符串表示。请参阅this SO 问题。正如我在之前的评论中告诉你的那样,如果可以的话,我很乐意帮助你。拜托,如果你能和我一起聊天,我将不胜感激。我在 CET 时区。例如,如果它也适合您,那么大约这个时间就可以了。如果没有,请在您有时间的时候告诉我
  • 嗨@cyber101。很抱歉,我会尽我所能帮助你,但我不想私下联系任何人。希望你能理解。我建议您开始聊天,但在这里,在 SO 中,使对话更加动态并尝试更快地解决问题。你可以吗?你什么时候有时间?另外,我更新了答案。尽管您提供的NamespaceContext 实现看起来不错,但请首先尝试一些更简单的方法,以尽量减少出错的可能性。例如,考虑答案中提供的那个。请问,你可以试试吗?
  • 我会在太平洋标准时间今晚前试一试。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-14
  • 1970-01-01
  • 2023-03-22
  • 2013-12-05
  • 1970-01-01
相关资源
最近更新 更多