使用STaX将xml转换为另一个xml需要花费大量时间

时间:2011-04-06 15:20:13

标签: java xml xslt stax

我正在使用以下代码将大型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());
    }
}

}

my_template是这样的:

<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>

如果在处理输入流时使用身份转换器(使用tranfsFactory.newTransformer()而不是transFactory(XSL)),则会生成输出。而不是我的模板,没有办法..变换器读取所有输入,然后开始产生输出(当然,大量的流经常在结果之前出现内存不足。

任何想法?我吓坏了..我无法理解我的代码中的错误/ xslt

非常感谢提前!!

6 个答案:

答案 0 :(得分:8)

XSLT 1.0和2.0在完整XML的树数据模型上运行,因此XSLT 1.0和2.0处理器通常将完整的XML输入文档读入树中,然后创建一个随后序列化的结果树。您似乎假设使用StAX更改了XSLT的行为,但我不认为是这种情况,XSLT处理器构建树,因为stylessheet可能需要复杂的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处理器测试是否会改变性能。

答案 1 :(得分:3)

使用XSLT可能不是最好的方法,因为其他人已经指出您的解决方案要求处理器在写出输出之前将整个文档读入内存。您可能希望考虑使用SAX解析器顺序读取每个节点,执行所需的任何转换(必要时使用数据驱动映射)并写出转换后的数据。这样就无需在内存中创建整个文档树,并且可以显着加快处理速度,因为您没有尝试构建要写出的复杂文档。

问问自己输出格式是否简单和稳定,然后重新考虑使用XSLT。对于常规数据的大型数据集,您可能还希望考虑XML是否是用于传输信息的良好文件格式。

答案 2 :(得分:2)

  

变换器读取所有输入,然后开始产生输出(当然,在结果出现之前,通常会出现内存不足的情况。

     

任何想法?

如果您发现完成此工作需要很长时间,那么您需要重新设计自己的任务方法,以避免在开始处理输出文件之前读取整个输入文件。没有任何东西可以通过你的代码调整,以使它神奇地更快 - 你需要解决你的算法的核心。

答案 3 :(得分:1)

您使用XSL进行的转换有多复杂?您可以单独使用StAX进行相同的转换吗?

使用StAX,编写解析器以匹配特定节点,然后在此时写入的输出流中插入,更改或删除节点非常容易。因此,您可以单独使用StAX,而不是使用XSL进行转换。这样你就可以从API的流媒体特性中受益(不在内存中缓冲大树),因此不存在内存问题。

顺便提一下,this最近对另一个问题的回答可能对此有所帮助。

答案 4 :(得分:1)

正如其他人指出的那样,使用Stax不会改变XSLT的工作方式:它在开始任何工作之前首先读取所有内容。 如果需要使用非常大的文件,则必须使用XSLT之外的其他内容。

然后是不同的选择:

答案 5 :(得分:0)

尝试apache xsltc以获得更好的性能 - 它使用代码生成来简单转换。

您的XSLt转换看起来非常简单,您的输入格式也是如此 - 当然您可以进行StAX / SAX手动处理并获得非常好的性能提升。