VTD 无法评估 "find all empty nodes with no attributes" xpath
VTD fails to evaluate a "find all empty nodes with no attributes" xpath
我在使用 2.13.4 版本的 vtd-xml 时发现了一个错误(我认为)。好吧,简而言之,我有以下代码片段:
String test = "<catalog><description></description></catalog>";
VTDGen vg = new VTDGen();
vg.setDoc(test.getBytes("UTF-8"));
vg.parse(true);
VTDNav vn = vg.getNav();
//get nodes with no childs, text and attributes
String xpath = "/catalog//*[not(child::node()) and not(child::text()) and count(@*)=0]";
AutoPilot ap = new AutoPilot(vn);
ap.selectXPath(xpath);
//block inside while is never executed
while(ap.evalXPath()!=-1) {
System.out.println("current node "+vn.toRawString(vn.getCurrentIndex()));
}
这不起作用(=没有找到任何节点,而它应该找到 "description")。如果我使用自闭标签,上面的代码有效:
String test = "<catalog><description/></catalog>";
重点是每个 xpath 评估器都适用于 xml 的两个版本。可悲的是,我从外部来源收到 xml,所以我无能为力......
打破 xpath 我注意到评估两者
/catalog//*[not(child::node())]
和
/catalog//*[not(child::text())]
结果为 false。作为额外的一点,我尝试了类似的东西:
String xpath = "/catalog/description/text()";
ap.selectXpath(xpath);
if(ap.evalXPath()!=-1)
System.out.println(vn.toRawString(vn.getCurrentIndex()));
并且此打印为空 space,所以在某种程度上 VTD "thinks" 节点有文本,即使是空的但仍然是,而我预计没有匹配。有什么提示吗?
TL;DR
When I faced this issue, I was left mainly with three options (see below). I went for the second option : Use XMLModifier to fix the VTDNav. At the bottom of my answser, you'll find an implementation of this option and a sample output.
说来话长...
我遇到了同样的问题。以下是我首先想到的主要三个选项(按难度排序):
1。在 XML 源代码中将空元素变成自闭标签。
这个选项并不总是可行的(比如在 OP 的情况下)。而且,可能很难“pre-process”之前的xml。
2。使用 XMLModifier to fix the VTDNav.
用 xpath 表达式找到空元素,用自闭标签替换它们并重建 VTDNav。
2.bis 使用 XMLModifier#removeToken
,上述解决方案的较低级别变体将包括遍历 VTDNav 中的标记并删除不必要的标记
3。直接修补vtd-xml代码。
走这条路可能需要更多的努力和更多的时间。 IMO,优化后的 vtd-xml 代码乍一看并不容易掌握。
选项 1 对我来说不可行。我未能实施选项 2bis。 “不必要的”令牌仍然存在。我没有看选项 3,因为我不想修复一些(相当复杂的)第三方代码。
我只剩下选项 2。这是一个实现:
代码
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.ximpleware.AutoPilot;
import com.ximpleware.NavException;
import com.ximpleware.VTDException;
import com.ximpleware.VTDGen;
import com.ximpleware.VTDNav;
import com.ximpleware.XMLModifier;
@Test
public void turnEmptyElementsIntoSelfClosedTags() throws VTDException, IOException {
// STEP 1 : Load XML into VTDNav
// * Convert the initial xml code into a byte array
String xml = "<root><empty-element></empty-element><self-closed/><empty-element2 foo='bar'></empty-element2></root>";
byte[] ba = xml.getBytes(StandardCharsets.UTF_8);
// * Build VTDNav and dump it to screen
VTDGen vg = new VTDGen();
vg.setDoc(ba);
vg.parse(false); // Use `true' to activate namespace support
VTDNav nav = vg.getNav();
dump("BEFORE", nav);
// STEP 2 : Prepare to fix the VTDNAv
// * Prepare an autopilot to find empty elements
AutoPilot ap = new AutoPilot(nav);
ap.selectXPath("//*[count(child::node())=1][text()='']");
// * Prepare a simple regex matcher to create self closed tags
Matcher elementReducer = Pattern.compile("^<(.+)></.+>$").matcher("");
// STEP 3 : Fix the VTDNAv
// * Instanciate an XMLModifier on the VTDNav
XMLModifier xm = new XMLModifier(nav);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // baos will hold the elements to fix
String utf8 = StandardCharsets.UTF_8.name();
// * Find all empty elements and replace them
while (ap.evalXPath() != -1) {
nav.dumpFragment(baos);
String emptyElementXml = baos.toString(utf8);
String selfClosingTagXml = elementReducer.reset(emptyElementXml).replaceFirst("</>");
xm.remove();
xm.insertAfterElement(selfClosingTagXml);
baos.reset();
}
// * Rebuild VTDNav and dump it to screen
nav = xm.outputAndReparse(); // You MUST call this method to save all your changes
dump("AFTER", nav);
}
private void dump(String msg,VTDNav nav) throws NavException, IOException {
System.out.print(msg + ":\n ");
nav.dumpFragment(System.out);
System.out.print("\n\n");
}
输出
BEFORE:
<root><empty-element></empty-element><self-closed/><empty-element2 foo='bar'></empty-element2></root>
AFTER:
<root><empty-element/><self-closed/><empty-element2 foo='bar'/></root>
我在使用 2.13.4 版本的 vtd-xml 时发现了一个错误(我认为)。好吧,简而言之,我有以下代码片段:
String test = "<catalog><description></description></catalog>";
VTDGen vg = new VTDGen();
vg.setDoc(test.getBytes("UTF-8"));
vg.parse(true);
VTDNav vn = vg.getNav();
//get nodes with no childs, text and attributes
String xpath = "/catalog//*[not(child::node()) and not(child::text()) and count(@*)=0]";
AutoPilot ap = new AutoPilot(vn);
ap.selectXPath(xpath);
//block inside while is never executed
while(ap.evalXPath()!=-1) {
System.out.println("current node "+vn.toRawString(vn.getCurrentIndex()));
}
这不起作用(=没有找到任何节点,而它应该找到 "description")。如果我使用自闭标签,上面的代码有效:
String test = "<catalog><description/></catalog>";
重点是每个 xpath 评估器都适用于 xml 的两个版本。可悲的是,我从外部来源收到 xml,所以我无能为力...... 打破 xpath 我注意到评估两者
/catalog//*[not(child::node())]
和
/catalog//*[not(child::text())]
结果为 false。作为额外的一点,我尝试了类似的东西:
String xpath = "/catalog/description/text()";
ap.selectXpath(xpath);
if(ap.evalXPath()!=-1)
System.out.println(vn.toRawString(vn.getCurrentIndex()));
并且此打印为空 space,所以在某种程度上 VTD "thinks" 节点有文本,即使是空的但仍然是,而我预计没有匹配。有什么提示吗?
TL;DR
When I faced this issue, I was left mainly with three options (see below). I went for the second option : Use XMLModifier to fix the VTDNav. At the bottom of my answser, you'll find an implementation of this option and a sample output.
说来话长...
我遇到了同样的问题。以下是我首先想到的主要三个选项(按难度排序):
1。在 XML 源代码中将空元素变成自闭标签。
这个选项并不总是可行的(比如在 OP 的情况下)。而且,可能很难“pre-process”之前的xml。
2。使用 XMLModifier to fix the VTDNav.
用 xpath 表达式找到空元素,用自闭标签替换它们并重建 VTDNav。
2.bis 使用 XMLModifier#removeToken
,上述解决方案的较低级别变体将包括遍历 VTDNav 中的标记并删除不必要的标记3。直接修补vtd-xml代码。
走这条路可能需要更多的努力和更多的时间。 IMO,优化后的 vtd-xml 代码乍一看并不容易掌握。
选项 1 对我来说不可行。我未能实施选项 2bis。 “不必要的”令牌仍然存在。我没有看选项 3,因为我不想修复一些(相当复杂的)第三方代码。
我只剩下选项 2。这是一个实现:
代码
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.ximpleware.AutoPilot;
import com.ximpleware.NavException;
import com.ximpleware.VTDException;
import com.ximpleware.VTDGen;
import com.ximpleware.VTDNav;
import com.ximpleware.XMLModifier;
@Test
public void turnEmptyElementsIntoSelfClosedTags() throws VTDException, IOException {
// STEP 1 : Load XML into VTDNav
// * Convert the initial xml code into a byte array
String xml = "<root><empty-element></empty-element><self-closed/><empty-element2 foo='bar'></empty-element2></root>";
byte[] ba = xml.getBytes(StandardCharsets.UTF_8);
// * Build VTDNav and dump it to screen
VTDGen vg = new VTDGen();
vg.setDoc(ba);
vg.parse(false); // Use `true' to activate namespace support
VTDNav nav = vg.getNav();
dump("BEFORE", nav);
// STEP 2 : Prepare to fix the VTDNAv
// * Prepare an autopilot to find empty elements
AutoPilot ap = new AutoPilot(nav);
ap.selectXPath("//*[count(child::node())=1][text()='']");
// * Prepare a simple regex matcher to create self closed tags
Matcher elementReducer = Pattern.compile("^<(.+)></.+>$").matcher("");
// STEP 3 : Fix the VTDNAv
// * Instanciate an XMLModifier on the VTDNav
XMLModifier xm = new XMLModifier(nav);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // baos will hold the elements to fix
String utf8 = StandardCharsets.UTF_8.name();
// * Find all empty elements and replace them
while (ap.evalXPath() != -1) {
nav.dumpFragment(baos);
String emptyElementXml = baos.toString(utf8);
String selfClosingTagXml = elementReducer.reset(emptyElementXml).replaceFirst("</>");
xm.remove();
xm.insertAfterElement(selfClosingTagXml);
baos.reset();
}
// * Rebuild VTDNav and dump it to screen
nav = xm.outputAndReparse(); // You MUST call this method to save all your changes
dump("AFTER", nav);
}
private void dump(String msg,VTDNav nav) throws NavException, IOException {
System.out.print(msg + ":\n ");
nav.dumpFragment(System.out);
System.out.print("\n\n");
}
输出
BEFORE:
<root><empty-element></empty-element><self-closed/><empty-element2 foo='bar'></empty-element2></root>
AFTER:
<root><empty-element/><self-closed/><empty-element2 foo='bar'/></root>