java使用STaX将xml转换为另一个xml需要很多时间
我使用以下代码将一个大的xml流转换为另一个流:
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stax.StAXResult;
import javax.xml.transform.stax.StAXSource;
public class TryMe
{
public static void main (final String[] args)
{
XMLInputFactory inputFactory = null;
XMLEventReader eventReaderXSL = null;
XMLEventReader eventReaderXML = null;
XMLOutputFactory outputFactory = null;
XMLEventWriter eventWriter = null;
Source XSL = null;
Source XML = null;
inputFactory = XMLInputFactory.newInstance();
outputFactory = XMLOutputFactory.newInstance();
inputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", Boolean.TRUE);
inputFactory.setProperty("javax.xml.stream.isNamespaceAware", Boolean.TRUE);
inputFactory.setProperty("javax.xml.stream.isReplacingEntityReferences", Boolean.TRUE);
try
{
eventReaderXSL = inputFactory.createXMLEventReader("my_template",
new InputStreamReader(TryMe.class.getResourceAsStream("my_template.xsl")));
eventReaderXML = inputFactory.createXMLEventReader("big_one", new InputStreamReader(
TryMe.class.getResourceAsStream("big_one.xml")));
}
catch (final javax.xml.stream.XMLStreamException e)
{
System.out.println(e.getMessage());
}
// get a TransformerFactory object
final TransformerFactory transfFactory = TransformerFactory.newInstance();
// define the Source object for the stylesheet
try
{
XSL = new StAXSource(eventReaderXSL);
}
catch (final javax.xml.stream.XMLStreamException e)
{
System.out.println(e.getMessage());
}
Transformer tran2 = null;
// get a Transformer object
try
{
tran2 = transfFactory.newTransformer(XSL);
}
catch (final javax.xml.transform.TransformerConfigurationException e)
{
System.out.println(e.getMessage());
}
// define the Source object for the XML document
try
{
XML = new StAXSource(eventReaderXML);
}
catch (final javax.xml.stream.XMLStreamException e)
{
System.out.println(e.getMessage());
}
// create an XMLEventWriter object
try
{
eventWriter = outputFactory.createXMLEventWriter(new OutputStreamWriter(System.out));
}
catch (final javax.xml.stream.XMLStreamException e)
{
System.out.println(e.getMessage());
}
// define the Result object
final Result XML_r = new StAXResult(eventWriter);
// call the transform method
try
{
tran2.transform(XML, XML_r);
}
catch (final javax.xml.transform.TransformerException e)
{
System.out.println(e.getMessage());
}
// clean up
try
{
eventReaderXSL.close();
eventReaderXML.close();
eventWriter.close();
}
catch (final javax.xml.stream.XMLStreamException e)
{
System.out.println(e.getMessage());
}
}
}
我的_模板如下所示:
<xsl:stylesheet version = '1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:preserve-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@k8[parent::point]">
<xsl:attribute name="k8">
<xsl:value-of select="'xxxxxxxxxxxxxx'"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
xml是一个很长的列表
<data>
<point .... k8="blablabla" ... ></point>
<point .... k8="blablabla" ... ></point>
<point .... k8="blablabla" ... ></point>
....
<point .... k8="blablabla" ... ></point>
</data>
如果在处理输入流时使用标识转换器(使用TransfFactory.newTransformer()而不是transFactory(XSL)),则会生成输出。取而代之的是我的模板,没有办法。。转换器读取所有输入,然后开始产生输出(当然,在产生结果之前通常会出现大量内存不足的情况)
有什么想法吗??我吓坏了。。我无法理解我的代码/xslt中的错误
非常感谢
# 1 楼答案
尝试apache xsltc以获得更好的性能——它使用代码生成来简化转换
XSLt转换看起来非常简单,输入格式也非常简单——当然,您可以进行StAX/SAX手动处理,并获得非常好的性能提升
# 2 楼答案
正如其他人所指出的,使用Stax不会改变XSLT的工作方式:它在开始任何工作之前先读取所有内容。 如果需要处理非常大的文件,那么就必须使用XSLT以外的东西
然后是不同的选择:
# 3 楼答案
使用XSL进行的转换有多复杂?你能单独使用StAX进行同样的转换吗
有了StAX,编写一个解析器来匹配特定节点,然后在输出流中插入、更改或删除节点是非常容易的。因此,不必使用XSL进行转换,您可以单独使用StAX。通过这种方式,您可以受益于API的流性质(不在内存中缓冲大型树),因此不会出现内存问题
顺便说一句,最近对另一个问题的回答可能会对你有所帮助
# 4 楼答案
XSLT 1.0和2.0在完整XML的树数据模型上运行,因此XSLT 1.0和2.0处理器通常将完整的XML输入文档读入树中,并创建一个结果树,然后进行序列化。您似乎认为使用StAX会改变XSLT的行为,但我不认为是这样,XSLT处理器会构建树,因为样式表可能需要复杂的XPath导航器,比如前面的或前面的同级
然而,在使用Java时,可以查看Saxon 9.3及其experimental XSLT 3.0 streaming support,这样在处理非常大的XML输入文档时,就不应该耗尽内存
XSLT中不寻常的部分是
<xsl:template match="@k8[parent::point]">
,通常简单地写为<xsl:template match="point/@k8">
,但您需要使用XSLT处理器测试这是否会改变性能# 5 楼答案
使用XSLT可能不是最好的方法,因为其他人已经指出,您的解决方案要求处理器在输出之前将整个文档读入内存。您可能希望考虑使用SAX解析器在每个节点中顺序读取,执行所需的任何转换(如果需要的话,使用数据驱动映射)并写出转换后的数据。这避免了在内存中创建整个文档树的要求,并且可以显著加快处理速度,因为您不需要构建复杂的文档来写出
问问自己输出格式是否简单稳定,然后重新考虑XSLT的使用。对于常规数据的大型数据集,您可能还希望考虑XML是否是传输信息的好文件格式。
# 6 楼答案
如果你发现这项工作需要很长时间才能完成,那么你需要重新设计你的任务方法,以避免在开始处理输出文件之前读取整个输入文件。没有什么可以通过调整代码来让它神奇地更快——你需要解决算法的核心问题