如何在Groovy或Java中按元素“路径”过滤XML字符串
我有一个对象,它当前使用JAXB从JavaPOJO映射到XML。一旦有了XML,我偶尔需要根据用户的输入将其缩减为一组选定的元素。结果应该是只包含指定字段的XML 我曾经遇到过许多类似的用例,我们使用SAX过滤器,但是它们看起来非常复杂,答案并不能满足我的需要。最接近的示例是,它从结果中排除了单个路径。我想要相反的-白名单一个元素的选择列表 示例对象:School.xml如何在Groovy或Java中按元素“路径”过滤XML字符串,java,xml,groovy,sax,Java,Xml,Groovy,Sax,我有一个对象,它当前使用JAXB从JavaPOJO映射到XML。一旦有了XML,我偶尔需要根据用户的输入将其缩减为一组选定的元素。结果应该是只包含指定字段的XML 我曾经遇到过许多类似的用例,我们使用SAX过滤器,但是它们看起来非常复杂,答案并不能满足我的需要。最接近的示例是,它从结果中排除了单个路径。我想要相反的-白名单一个元素的选择列表 示例对象:School.xml <SchoolInfo RefId="34060F68BE3942F1B1264E6D2CC3C353">
<SchoolInfo RefId="34060F68BE3942F1B1264E6D2CC3C353">
<LocalId>57</LocalId>
<SchoolName>Foobar School of Technology</SchoolName>
<Principal>
<FirstName>Bob</FirstName>
<LastName>Smith</LastName>
</Principal>
<StateProvinceId>34573</StateProvinceId>
<LEAInfoRefId>340666687E3942F1B1264E1223453C353</LEAInfoRefId>
<PhoneNumberList>
<PhoneNumber Type="0096">
<Number>555-832-5555</Number>
</PhoneNumber>
<PhoneNumber Type="0096">
<Number>555-999-5555</Number>
</PhoneNumber>
</PhoneNumberList>
</SchoolInfo>
将以下输入作为过滤器:
List<String> filter = [
"LocalId",
"SchoolName",
"Principal/FirstName",
"PhoneNumberList/PhoneNumber/Number",
]
我需要输出为:
<SchoolInfo RefId="34060F68BE3942F1B1264E6D2CC3C353">
<LocalId>57</LocalId>
<SchoolName>Foobar School of Technology</SchoolName>
<Principal>
<FirstName>Bob</FirstName>
</Principal>
<PhoneNumberList>
<PhoneNumber Type="0096">
<Number>555-832-5555</Number>
</PhoneNumber>
<PhoneNumber Type="0096">
<Number>555-999-5555</Number>
</PhoneNumber>
</PhoneNumberList>
</SchoolInfo>
实现这一目标的最佳库是什么?SAX过滤感觉很复杂,考虑到动态过滤,XSLT似乎并不适合
非常感谢帮助我更进一步的示例。这是完成白名单的代码。。。它基于XPath和VTD-XML。它的输出有缩进问题。。。这是第一次强调正确性
import com.ximpleware.*;
import java.io.*;
import java.util.*;
public class whiteList {
public static void main(String[] s) throws VTDException, IOException{
VTDGen vg = new VTDGen();
List <String> filter = Arrays.asList("LocalId",
"SchoolName",
"Principal/FirstName",
"PhoneNumberList/PhoneNumber/Number");
if (!vg.parseFile("d:\\xml\\schoolInfo.xml", false)){
return;
}
VTDNav vn = vg.getNav();
FastIntBuffer fib = new FastIntBuffer();
// build a bitmap for the entire token pool consisting of elements
int i,k;
for (i=0;i<vn.getTokenCount();i++){
if (vn.getTokenType(i)==VTDNav.TOKEN_STARTING_TAG){
fib.append(0x1);// b'11 since it is a white list,
}else{
fib.append(0);
}
}
AutoPilot ap = new AutoPilot(vn);
AutoPilot ap1= new AutoPilot(vn);
ap1.selectXPath("descendant::*");// mark descendant as keep
for (int j=0;j<filter.size();j++){
ap.selectXPath(filter.get(j));
while((i=ap.evalXPath())!=-1){
fib.modifyEntry(i, 0x3);
vn.push();
do{
if( vn.getTokenDepth(vn.getCurrentIndex())>=0)
fib.modifyEntry(vn.getCurrentIndex(), 0x3);
else
break;
}while(vn.toElement(VTDNav.P));
vn.pop();
vn.push();
while((k=ap1.evalXPath())!=-1){
fib.modifyEntry(k, 0x3);
}
ap1.resetXPath();
vn.pop();
}
ap.resetXPath();
}
//remove those not on the whitelist
XMLModifier xm = new XMLModifier(vn);
for (int j=0;j<fib.size();j++){
if (fib.intAt(j)==0x1){
vn.recoverNode(j);
xm.remove();
}
}
xm.output("d:\\xml\\newSchoolInfo.xml");
}
}
所有Groovy:
import groovy.xml.XmlUtil
def xml = '''<SchoolInfo RefId="34060F68BE3942F1B1264E6D2CC3C353">
<LocalId>57</LocalId>
<SchoolName>Foobar School of Technology</SchoolName>
<Principal>
<FirstName>Bob</FirstName>
<LastName>Smith</LastName>
</Principal>
<StateProvinceId>34573</StateProvinceId>
<LEAInfoRefId>340666687E3942F1B1264E1223453C353</LEAInfoRefId>
<PhoneNumberList>
<PhoneNumber Type="0096">
<Number>555-832-5555</Number>
</PhoneNumber>
<PhoneNumber Type="0096">
<Number>555-999-5555</Number>
</PhoneNumber>
</PhoneNumberList>
</SchoolInfo>'''
def node = new XmlParser().parseText(xml)
def whitelist = [ 'LocalId', 'SchoolName', 'Principal/FirstName', "PhoneNumberList/PhoneNumber/Number" ]*.split('/')
def void loveRemovalMachine(node, whitelist) {
def elementNamesToKeep = whitelist*.head()
println "Retaining nodes ${elementNamesToKeep} for node $node"
def nodesToRemove = node.'*'.findAll { child -> !elementNamesToKeep.contains(child.name()) }
nodesToRemove.each { node.remove it }
def nextWhitelist = whitelist*.tail().findAll { it }
println "Next level: $nextWhitelist"
if (!nextWhitelist) {
return
}
// The "*" operator seems to return text nodes...very stupid.
node.'*:*'.each { loveRemovalMachine it, nextWhitelist }
}
loveRemovalMachine node, whitelist
XmlUtil.serialize node
Groovy应该擅长使用XmlParser或MarkupBuild。看到一些示例了吗?您是在寻找能够实现这一点的代码示例,还是仅仅在libs上寻求建议?@vtd xml author一个代码示例会让人惊讶,但我并没有要求任何人为我做这项工作。关于正确使用的库和库中的方法的建议是我正在寻找的。我不太清楚您对筛选的定义。。。如果在phonenumber元素下,有另一个名为address的元素。。。您的筛选器没有解决哪个问题,是否应该保留它?@vtd xml author筛选器需要是一个白名单。因此,只保留包含的元素。我不确定的是属性,但我可以在元素本身完成后弄清楚。在这种情况下,不包括address元素。但是,如果你要询问PhoneNumberList/PhoneNumber,它确实会变得很棘手。在这种情况下,应该包括PhoneNumber的所有子元素。我现在正在测试这个。一个直接的修改是我需要从一个字符串中读取,并作为一个字符串输出。我假设我可以使用ByteArrayInputStream及其等价物?当我调用ByteArrayInputStream上的xm.output时,这实际上会导致NullPointerException。我在执行之后很难跟上,但是当它尝试写入输出时,似乎没有VTD令牌?您能发布到目前为止的代码吗?要读取字符串,您需要将其转换为字节数组,这很容易做到。。。下面是一篇博客文章,向您展示如何从字节数组解析xml…输出:结果:57 Foobar理工学院Bob Smith 555-832-5555 555-999-5555当然这可以用Java完成;如果您需要这样的代码示例,请告知。这是相同的想法:一个清理DOM的递归方法。在Java中会更加冗长。