我想将XML文件作为输入并输出相同的XML,除了对属性和文本的一些搜索/替换操作,基于匹配某些节点特征。
这方面最好的一般方法是什么,是否有教程?
DOM已经出局,因为我无法保证能够将整个内容保留在内存中。
我不介意使用SAX或StAX,除了我希望默认行为是传递无操作过滤器;我曾经做过类似于StAX的事情,这很痛苦,没有使用命名空间,我不知道是否包含了我需要处理的所有案例。
我认为XSLT不起作用(但不确定),因为它是声明性的,我需要在确定输出上要发出的文本/属性时进行一些程序计算。
(人为例子:
假设我正在寻找XPath为/group/item/@number
的所有节点,并希望将number
属性评估为整数,使用方法public List<Integer> factorize(int i)
对其进行计算,将因子列表转换为以空格分隔的字符串,并将属性factors
添加到相应的/group/item
节点?
输入:
<group name="beatles"><item name="paul" number="64"></group>
<group name="rolling stones"><item name="mick" number="19"></group>
<group name="who"><item name="roger" number="515"></group>
预期产出:
<group name="beatles"><item name="paul" number="64" factors="2 2 2 2 2 2"></group>
<group name="rolling stones"><item name="mick" number="19" factors="19"></group>
<group name="who"><item name="roger" number="515" factors="103 5"></group>
)
更新:我让StAX XMLEventReader / Writer方法轻松工作,但它不会保留某些在我的应用程序中很重要的格式怪癖。 (我想保存/加载XML的程序不支持有效的XML文件。&gt; :( argh。)有没有办法处理XML,最大限度地减少输入和输出之间的文本差异?(至少在字符数据方面) 。)
答案 0 :(得分:3)
XSLT似乎是您正在做的事情的合适模型。考虑使用带有过程扩展的XSLT。
如果您真的无法将整个文档保留在内存中,Saxon是您唯一的XSLT选择。很可能无论你需要做什么计算都可以在XSLT中完成 - 但如果没有,那么编写自己的extension functions并不是很难。
答案 1 :(得分:2)
我发现Apache Digester对基于规则的XML解析有很大的帮助。
更新:如果它是您关注的过滤和输出,请查看关于相同问题的Developerworks上的这组文章。特别相关的是部分2,3和4。摘要:使用SAX,XMLFilter和XMLWriter。
虽然我认为这在技术上非常适合XSLT,但我总是发现难以调试复杂的转换。 YMMV: - )
进一步更新: XMLWriter可从here获得。我不知道你对SAX的特殊困难是什么。我创建了一个文件groups.xml
,其中包含:
<groups>
<group name="beatles"><item name="paul" number="64"/></group>
<group name="rolling stones"><item name="mick" number="19"/></group>
<group name="who"><item name="roger" number="515"/></group>
</groups>
请注意,我必须进行一些更改才能使格式良好的XML。然后,我敲了这个简单的Jython脚本groups.py
来说明如何解决问题:
import java.io
import org.xml.sax.helpers
import sys
sys.path.append("xml-writer.jar")
import com.megginson.sax
def get_factors(n):
return "factors for %s" % n
class MyFilter(org.xml.sax.helpers.XMLFilterImpl):
def startElement(self, uri, localName, qName, attrs):
if qName == "item":
newAttrs = org.xml.sax.helpers.AttributesImpl(attrs)
n = attrs.length
for i in range(n):
name = attrs.getLocalName(i)
if name == "number":
newAttrs.addAttribute("", "factors", "factors",
"CDATA",
get_factors(attrs.getValue(i)))
attrs = newAttrs
#call superclass method...
org.xml.sax.helpers.XMLFilterImpl.startElement(self, uri, localName,
qName, attrs)
source = org.xml.sax.InputSource(java.io.FileInputStream("groups.xml"))
reader = org.xml.sax.helpers.XMLReaderFactory.createXMLReader()
filter = MyFilter(reader)
writer = com.megginson.sax.XMLWriter(filter,
java.io.FileWriter("output.xml"))
writer.parse(source)
显然,我已经嘲笑了因子发现功能,因为我相信这个例子纯粹是说明性的。该脚本会显示groups.xml
,应用过滤器并输出到output.xml
。我们来吧:
$ jython groups.py $ cat output.xml
<?xml version="1.0" standalone="yes"?>
<groups>
<group name="beatles"><item name="paul" number="64" factors="factors for 64"></item></group>
<group name="rolling stones"><item name="mick" number="19" factors="factors for 19"></item></group>
<group name="who"><item name="roger" number="515" factors="factors for 515"></item></group>
</groups>
完成工作?当然,您需要将此代码转录为Java。
答案 2 :(得分:1)
StAX应该适合你。管道输入输出非常简单;你只需要编写从XMLEventReader到XMLEventWriter的XMLEvent。
XMLEventFactory EVT_FACTORY;
XMLEventReader reader;
XMLEventWriter writer;
QName numberQName = new QName("number");
QName factorsQName = new QName("factors");
while(reader.hasNext()) {
XMLEvent e = reader.nextEvent();
if(e.isAttribute() && ((Attribute)e).getName().equals(numberQName)) {
String v = ((Attribute)e).getValue();
String factors = factorize(Integer.parseInt(v));
XMLEvent factorsAttr = EVT_FACTORY.createAttribute(factorsQName, factors);
writer.add(factorsAttr);
}
// pass through
writers.add(e);
}