java - 尝试使用 Java 和 Xpath 更新 XML 文档中任何选定节点的值
问题描述
我开发了 GUI 工具,将 XML 文档显示为可编辑的 JTree,用户可以在 JTree 中选择一个节点并尝试更改 XML 文档中的实际节点值。
我遇到的问题是构建尝试实际更新的正确 Xpath 查询。
这是 JTree 的 GUI,显示选择了哪个元素并且应该编辑:
它是一个非常大的 XML,所以这里是 XML 的折叠片段:
更新(忽略尝试 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: Query:/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) 返回 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
对象作为最终的绝望尝试,请参见下面的片段。
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!
解决方案
对于您在线执行的测试,您的 XML 文件似乎包含名称空间信息。
考虑到这些信息,您的两个 XPath 评估示例可能会起作用,也可能不起作用,这取决于几件事。
例如,您可能可以使用尝试 #4,如果您使用的是非命名空间感知(默认)DocumentBuilderFactory
并且您没有在 XPath 表达式中提供任何命名空间信息,那么 XPath 评估就足够了。
但是,如果应用相反的条件,尝试 #3 中的 XPath 评估也可能是足够的,即,您正在使用命名空间感知DocumentBuilderFactory
:
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
f.setNamespaceAware(true);
并且您在 XPath 表达式中提供命名空间信息和方便的NamespaceContext
实现。请参阅这个相关的 SO 问题和这篇很棒的 IBM 文章。
请注意,您不需要在 XML 文件和 XPath 表达式中提供相同的名称空间前缀,唯一的要求是 XML 中的名称空间感知(XPath 始终是名称空间感知的)。
鉴于这种情况,我认为您可以同时应用这两种方法。
在任何情况下,我认为问题可能与您处理实际文本替换的方式有关:您正在寻找具有value
属性的节点,并查看相关的XML 模式,该属性不存在。
请考虑以下方法:
// 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;
此代码假定所选节点是唯一的才能正常工作。
尽管可能不同,但请注意,JTree
如果您为 XML 中的重复元素定义相同的值,则从模型构造 XPath 选择器的方式可能会提供重复项。例如,考虑屏幕截图中的元素external_id_property_maps
。
为了避免这种情况,您可以在构造 XPath 选择器时采用不同的方法。
您的代码片段尚不清楚,但可能您正在DefaultMutableTreeNode
用作基本JTree
节点类型。如果是这种情况,您可以将所需的任意信息与每个节点相关联。
例如,考虑创建一个简单的 POJO,其中包含两个字段、Element
节点表示的名称以及某种唯一的、生成的、id,让我们命名它uid
或uuid
避免与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();
现在,创建一个JTree
与输入 XML 文件对应的结构。首先,让我们创建一个方便的 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']
它将再次找到正确的节点。
推荐阅读
- python - 从 Dataframe 中删除行会导致在 Python 中分发数据框
- javascript - 无法读取 null 的属性“textContent”
- php - 是否可以调试此 PHP
- node.js - 我们能否在前端 Angular&NodeJS 中包含 Neo4j 应用程序输出
- python - 如何将视频转换为毫秒帧,而不是使用 OpenCV 从基本帧速率转换
- linux - 如何通过 gdbus 发送 Dictionary 数据类型(a{ias})?
- javascript - 使用 javascript 滚动到 ID
- file - 如何使用 Google Apps 脚本和 Discord Webhook 将图像上传到 Discord?
- python - Python中的双向链表
- java - 经过训练的神经网络为所有评估行输出相同的结果