我想解析一个包含以下事件的xml元素:
<employees> <employee> <details> <name>Joe</name> <age>34</age> </details> <address> <street>test</street> <nr>12</nr> </address> </employee> <employee> <address>....</address> <details> <!-- note the changed order of elements! --> <age>24</age> <name>Sam</name> </details> </employee> </employees>
输出应该是csv:
name;age;street;nr
Joe,34,test,12
Sam,24,...
问题:当使用像stax/sax
这样的事件驱动的解析器时,我必须创建一个临时的Employee
bean,它的属性我在每个事件节点上设置,然后将bean转换为csv。
但是由于我的xml文件大小为几GB,我想防止为每个条目创建额外的bean对象。
因此我可能不得不使用普通的DOM
解析?纠正我,如果我错了,我很高兴有任何建议。
我尝试如下。问题是doc.getElementsByTagName("employees")
返回一个空节点列表,而我期望一个xml元素。为什么呢?
StringBuilder sb = new StringBuilder();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(xml)));
doc.getDocumentElement().normalize();
NodeList employees = doc.getElementsByTagName("employees");
for (int i = 0; i < employees.getLength(); i++) {
Node employee = employees.item(i);
if (employees.getNodeType() == Node.ELEMENT_NODE) {
NodeList employee = ((Element) employees).getElementsByTagName("employee");
for (int j = 0; j < employee.getLength(); j++) {
NodeList details = ((Element) employee).getElementsByTagName("details");
//the rest is pseudocode
for (details)
sb.append(getElements("name").item(0) + ",");
sb.append(getElements("age").item(0) + ",");
for (address)
sb.append(getElements("street").item(0) + ",");
sb.append(getElements("nr").item(0) + ",");
}
}
}
答案 0 :(得分:3)
DOM解决方案将使用大量内存,SAX / Stax解决方案将涉及编写和调试大量代码。这项工作的理想工具是XSLT 3.0可流式转换:
<xsl:transform version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:mode streamable="yes" on-no-match="shallow-skip"/>
<xsl:template match="employee">
<xsl:value-of select="copy-of(.)!(.//name, .//age, .//street, .//nr)"
separator=","/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:transform>
注意强>
我最初将选择表达式编写为copy-of(.)//(name, age, street, nr)
。这是不正确的,因为//
运算符会将结果排序为文档顺序,这是我们不想要的。使用!
和,
会小心避免排序。
答案 1 :(得分:1)
不要使用StringBuilder,而是立即写入文件(Files.newBufferedWriter)。
手动解析XML并不是什么大问题,因为似乎没有高度复杂性,也不需要基于XML的验证。
&
,它应该是XML中的&
。如果XML有效(您可以使用前面添加<?xml ...>
的Reader),则扫描XML将是:
XMLInputFactory f = XMLInputFactory.newInstance();
XMLStreamReader r = f.createXMLStreamReader( ... );
while(r.hasNext()) {
r.next();
}
这样可以轻松地为员工属性维护一个Map,从<employee>
开始到结束,在</employee>
进行验证和编写。