将 xml 转换为 cvs
Transform xml to cvs
我开发了通过 xsl 将 xml 转换为 cvs 的应用程序。我使用了 DOM API,但它的性能很差(在输入中我有 100000 xml 大小为 200kb-20mb)我尝试使用 SAX API,但我收到错误的结果转换后的输出。
Dom api:
@PostConstruct
public void init() throws ParserConfigurationException, TransformerConfigurationException {
styleSheet = new File("1.xsl");
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
stylesource = new StreamSource(styleSheet);
transformer = TransformerFactory.newInstance().newTransformer(stylesource);
}
public String transformXmlToCsv(String inputXml) {
String csv = null;
try {
InputSource is = new InputSource(new StringReader(inputXml));
Document document = builder.parse(is);
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(writer));
csv = writer.toString();
writer.close();
} catch (Exception e) {
LOGGER.error("Exception during transorming", e);
}
return csv;
}
}
SAX API:
public static void main(String[] args) throws Exception {
TransformerFactory transFact = TransformerFactory.newInstance( );
File xml = new File("019dc124-5057-43f3-aa5d-1d840536b1b5-1558467374000.xml");
File styleSheet = new File("1.xsl");
Result outputTarget = new StreamResult(new File("C:\proj\xmlparser\result.csv"));
Source stylesource = new StreamSource(styleSheet);
Transformer trans = transFact.newTransformer(stylesource);
InputSource is = new InputSource(new FileReader(xml));
Source xmlSource = new SAXSource(is);
trans.transform(xmlSource, outputTarget);
}
如果性能是您的问题,那么不要为您正在转换的每个文件创建新的 TransformerFactory
、DocumentBuilderFactory
等。实例化这些 JAXP 工厂 类 是出了名的昂贵,因为它涉及搜索类路径。您没有说文件的大小,但是如果您正在解析数十万个非常小的 XML 文件,那么性能的关键是重用同一个解析器; DOM-vs-SAX 的差异可以忽略不计。
我认为您可能只是 运行 成为关于正确使用 XSLT 到名称空间中的 select 元素的最常见问题的变体,我认为默认的 Java DocumentBuilder不识别名称空间,因此您的 XSLT 代码可能会将默认名称空间中的元素视为不在任何名称空间中,这样您的路径 root/rootnode/name
就可以工作。另一方面,使用 Sax,我认为 XSLT 处理器将看到您所说的默认命名空间中的元素,然后您的路径不再起作用,因为它们 select 没有命名空间中的元素。
要解决此问题,有两种方法:通过将 Saxon 9 HE(最新版本为 9.9)放在类路径中切换到 XSLT 2/3,然后使用例如xpath-default-namespace="http://example.com/ns"
作为 xsl:stylesheet
或 xsl:transform
根元素的属性。
或者,如果您坚持使用 XSLT 1,唯一的解决方法是在样式表中为该命名空间(例如 http://example.com/ns
)声明一个前缀(例如 pf1
),例如xmlns:pf1="http://example.com/ns"
然后更改所有 XPath 表达式和匹配模式以使用前缀,因此 root/rootnode/name
变为 pf1:object/pf1:rootnode/pf1:name
.
我开发了通过 xsl 将 xml 转换为 cvs 的应用程序。我使用了 DOM API,但它的性能很差(在输入中我有 100000 xml 大小为 200kb-20mb)我尝试使用 SAX API,但我收到错误的结果转换后的输出。 Dom api:
@PostConstruct
public void init() throws ParserConfigurationException, TransformerConfigurationException {
styleSheet = new File("1.xsl");
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
stylesource = new StreamSource(styleSheet);
transformer = TransformerFactory.newInstance().newTransformer(stylesource);
}
public String transformXmlToCsv(String inputXml) {
String csv = null;
try {
InputSource is = new InputSource(new StringReader(inputXml));
Document document = builder.parse(is);
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(writer));
csv = writer.toString();
writer.close();
} catch (Exception e) {
LOGGER.error("Exception during transorming", e);
}
return csv;
}
}
SAX API:
public static void main(String[] args) throws Exception {
TransformerFactory transFact = TransformerFactory.newInstance( );
File xml = new File("019dc124-5057-43f3-aa5d-1d840536b1b5-1558467374000.xml");
File styleSheet = new File("1.xsl");
Result outputTarget = new StreamResult(new File("C:\proj\xmlparser\result.csv"));
Source stylesource = new StreamSource(styleSheet);
Transformer trans = transFact.newTransformer(stylesource);
InputSource is = new InputSource(new FileReader(xml));
Source xmlSource = new SAXSource(is);
trans.transform(xmlSource, outputTarget);
}
如果性能是您的问题,那么不要为您正在转换的每个文件创建新的 TransformerFactory
、DocumentBuilderFactory
等。实例化这些 JAXP 工厂 类 是出了名的昂贵,因为它涉及搜索类路径。您没有说文件的大小,但是如果您正在解析数十万个非常小的 XML 文件,那么性能的关键是重用同一个解析器; DOM-vs-SAX 的差异可以忽略不计。
我认为您可能只是 运行 成为关于正确使用 XSLT 到名称空间中的 select 元素的最常见问题的变体,我认为默认的 Java DocumentBuilder不识别名称空间,因此您的 XSLT 代码可能会将默认名称空间中的元素视为不在任何名称空间中,这样您的路径 root/rootnode/name
就可以工作。另一方面,使用 Sax,我认为 XSLT 处理器将看到您所说的默认命名空间中的元素,然后您的路径不再起作用,因为它们 select 没有命名空间中的元素。
要解决此问题,有两种方法:通过将 Saxon 9 HE(最新版本为 9.9)放在类路径中切换到 XSLT 2/3,然后使用例如xpath-default-namespace="http://example.com/ns"
作为 xsl:stylesheet
或 xsl:transform
根元素的属性。
或者,如果您坚持使用 XSLT 1,唯一的解决方法是在样式表中为该命名空间(例如 http://example.com/ns
)声明一个前缀(例如 pf1
),例如xmlns:pf1="http://example.com/ns"
然后更改所有 XPath 表达式和匹配模式以使用前缀,因此 root/rootnode/name
变为 pf1:object/pf1:rootnode/pf1:name
.