使用 Saxon 修改 XPath 2.0 结果树
Modifying XPath 2.0 result trees using Saxon
我愿意
- add/remove/update elements/attributes/values 到 "subTree"
- 能够将更新的 "targetDoc" 保存回 "target" 文件位置。
- 确定哪种树模型最适合此 xpath + 树修改过程。
我想我应该能够以某种方式获得 MutableNodeInfo 对象,但我不知道该怎么做。我尝试使用 processor.setConfigurationProperty(FeatureKeys.TREE_MODEL, Builder.LINKED_TREE);但这仍然为我提供了 TinyElementImpl 的底层节点。我需要 xpath 2.0 以避免必须输入默认命名空间,这就是我使用 saxon s9api 而不是 Java 的默认 DOM 模型的原因。我还想尽可能避免使用 xslt/xquery,因为这些树修改是动态完成的,使 xslt/xquery 在我的情况下更加复杂。
public static void main(String[] args) {
// XML File namesspace URIs
Hashtable<String, String> namespaceURIs = new Hashtable<>();
namespaceURIs.put("def", "http://www.cdisc.org/ns/def/v2.0");
namespaceURIs.put("xmlns", "http://www.cdisc.org/ns/odm/v1.3");
namespaceURIs.put("xsi", "http://www.w3.org/2001/XMLSchema-instance");
namespaceURIs.put("xlink", "http://www.w3.org/1999/xlink");
namespaceURIs.put("", "http://www.cdisc.org/ns/odm/v1.3");
// The source/target xml document
String target = "Path to file.xml";
// An xpath string
String xpath = "/ODM/Study/MetaDataVersion/ItemGroupDef[@OID/string()='IG.TA']";
Processor processor = new Processor(true);
// I thought this tells the processor to use something other than
// TinyTree
processor.setConfigurationProperty(FeatureKeys.TREE_MODEL,
Builder.LINKED_TREE);
DocumentBuilder builder = processor.newDocumentBuilder();
XPathCompiler xpathCompiler = processor.newXPathCompiler();
for (Entry<String, String> entry : namespaceURIs.entrySet()) {
xpathCompiler.declareNamespace(entry.getKey(), entry.getValue());
}
try {
XdmNode targetDoc = builder.build(Paths.get(target).toFile());
XPathSelector selector = xpathCompiler.compile(xpath).load();
selector.setContextItem(targetDoc);
XdmNode subTree = (XdmNode) selector.evaluateSingle();
// The following prints: class
// net.sf.saxon.tree.tiny.TinyElementImpl
System.out.println(subTree.getUnderlyingNode().getClass());
/*
* Here, is where I would like to modify subtree and save modified doc
*/
} catch (SaxonApiException e) {
e.printStackTrace();
}
}
我认为您可以为 Saxon 提供一个 DOM 节点,并针对它提供 运行 XPath,但在这种情况下,您不使用 Saxon 本地树的文档生成器,而是构建一个 DOM 使用 javax.xml.parsers.DocumentBuilder
,一旦你有了一个 W3C DOM 节点,你就可以使用 Saxon DocumentBuilder
的 wrap
方法将它提供给 Saxon。这是从 Saxon 9.6 resources file 中的文件 S9APIExamples.java 中获取的示例代码:
// Build the DOM document
File file = new File("data/books.xml");
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
dfactory.setNamespaceAware(true);
javax.xml.parsers.DocumentBuilder docBuilder;
try {
docBuilder = dfactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new SaxonApiException(e);
}
Document doc;
try {
doc = docBuilder.parse(new InputSource(file.toURI().toString()));
} catch (SAXException e) {
throw new SaxonApiException(e);
} catch (IOException e) {
throw new SaxonApiException(e);
}
// Compile the XPath Expression
Processor proc = new Processor(false);
DocumentBuilder db = proc.newDocumentBuilder();
XdmNode xdmDoc = db.wrap(doc);
XPathCompiler xpath = proc.newXPathCompiler();
XPathExecutable xx = xpath.compile("//ITEM/TITLE");
// Run the XPath Expression
XPathSelector selector = xx.load();
selector.setContextItem(xdmDoc);
for (XdmItem item : selector) {
XdmNode node = (XdmNode) item;
org.w3c.dom.Node element = (org.w3c.dom.Node) node.getExternalNode();
System.out.println(element.getTextContent());
}
还有一些示例展示了如何将 Saxon 与 JDOM 和其他可变树实现一起使用,但我认为您需要 Saxon PE 或 EE 才能直接支持这些实现。
Saxon 中的 MutableNodeInfo 接口是专门为满足 XQuery 更新的需要而设计的,我建议不要尝试直接从 Java 中使用它;在处理 XQuery Update 以外的方法调用时,实现不太可能是健壮的。
事实上,Saxon NodeInfo 接口通常被设计为 XPath 的目标,而不是用户编写的 Java 代码。因此,我建议使用第三方树模型;我最喜欢的是 JDOM2 和 XOM。这两者都允许您将直接 Java 导航和更新与使用 Saxon 的 XPath 2.0 导航混合使用。
我愿意
- add/remove/update elements/attributes/values 到 "subTree"
- 能够将更新的 "targetDoc" 保存回 "target" 文件位置。
- 确定哪种树模型最适合此 xpath + 树修改过程。
我想我应该能够以某种方式获得 MutableNodeInfo 对象,但我不知道该怎么做。我尝试使用 processor.setConfigurationProperty(FeatureKeys.TREE_MODEL, Builder.LINKED_TREE);但这仍然为我提供了 TinyElementImpl 的底层节点。我需要 xpath 2.0 以避免必须输入默认命名空间,这就是我使用 saxon s9api 而不是 Java 的默认 DOM 模型的原因。我还想尽可能避免使用 xslt/xquery,因为这些树修改是动态完成的,使 xslt/xquery 在我的情况下更加复杂。
public static void main(String[] args) {
// XML File namesspace URIs
Hashtable<String, String> namespaceURIs = new Hashtable<>();
namespaceURIs.put("def", "http://www.cdisc.org/ns/def/v2.0");
namespaceURIs.put("xmlns", "http://www.cdisc.org/ns/odm/v1.3");
namespaceURIs.put("xsi", "http://www.w3.org/2001/XMLSchema-instance");
namespaceURIs.put("xlink", "http://www.w3.org/1999/xlink");
namespaceURIs.put("", "http://www.cdisc.org/ns/odm/v1.3");
// The source/target xml document
String target = "Path to file.xml";
// An xpath string
String xpath = "/ODM/Study/MetaDataVersion/ItemGroupDef[@OID/string()='IG.TA']";
Processor processor = new Processor(true);
// I thought this tells the processor to use something other than
// TinyTree
processor.setConfigurationProperty(FeatureKeys.TREE_MODEL,
Builder.LINKED_TREE);
DocumentBuilder builder = processor.newDocumentBuilder();
XPathCompiler xpathCompiler = processor.newXPathCompiler();
for (Entry<String, String> entry : namespaceURIs.entrySet()) {
xpathCompiler.declareNamespace(entry.getKey(), entry.getValue());
}
try {
XdmNode targetDoc = builder.build(Paths.get(target).toFile());
XPathSelector selector = xpathCompiler.compile(xpath).load();
selector.setContextItem(targetDoc);
XdmNode subTree = (XdmNode) selector.evaluateSingle();
// The following prints: class
// net.sf.saxon.tree.tiny.TinyElementImpl
System.out.println(subTree.getUnderlyingNode().getClass());
/*
* Here, is where I would like to modify subtree and save modified doc
*/
} catch (SaxonApiException e) {
e.printStackTrace();
}
}
我认为您可以为 Saxon 提供一个 DOM 节点,并针对它提供 运行 XPath,但在这种情况下,您不使用 Saxon 本地树的文档生成器,而是构建一个 DOM 使用 javax.xml.parsers.DocumentBuilder
,一旦你有了一个 W3C DOM 节点,你就可以使用 Saxon DocumentBuilder
的 wrap
方法将它提供给 Saxon。这是从 Saxon 9.6 resources file 中的文件 S9APIExamples.java 中获取的示例代码:
// Build the DOM document
File file = new File("data/books.xml");
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
dfactory.setNamespaceAware(true);
javax.xml.parsers.DocumentBuilder docBuilder;
try {
docBuilder = dfactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new SaxonApiException(e);
}
Document doc;
try {
doc = docBuilder.parse(new InputSource(file.toURI().toString()));
} catch (SAXException e) {
throw new SaxonApiException(e);
} catch (IOException e) {
throw new SaxonApiException(e);
}
// Compile the XPath Expression
Processor proc = new Processor(false);
DocumentBuilder db = proc.newDocumentBuilder();
XdmNode xdmDoc = db.wrap(doc);
XPathCompiler xpath = proc.newXPathCompiler();
XPathExecutable xx = xpath.compile("//ITEM/TITLE");
// Run the XPath Expression
XPathSelector selector = xx.load();
selector.setContextItem(xdmDoc);
for (XdmItem item : selector) {
XdmNode node = (XdmNode) item;
org.w3c.dom.Node element = (org.w3c.dom.Node) node.getExternalNode();
System.out.println(element.getTextContent());
}
还有一些示例展示了如何将 Saxon 与 JDOM 和其他可变树实现一起使用,但我认为您需要 Saxon PE 或 EE 才能直接支持这些实现。
Saxon 中的 MutableNodeInfo 接口是专门为满足 XQuery 更新的需要而设计的,我建议不要尝试直接从 Java 中使用它;在处理 XQuery Update 以外的方法调用时,实现不太可能是健壮的。
事实上,Saxon NodeInfo 接口通常被设计为 XPath 的目标,而不是用户编写的 Java 代码。因此,我建议使用第三方树模型;我最喜欢的是 JDOM2 和 XOM。这两者都允许您将直接 Java 导航和更新与使用 Saxon 的 XPath 2.0 导航混合使用。