2

我正在使用以下代码将一个大的 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 出了什么问题

提前谢谢了!!

4

6 回答 6

8

XSLT 1.0 和 2.0 对完整 XML 的树数据模型进行操作,因此 XSLT 1.0 和 2.0 处理器通常将完整的 XML 输入文档读入树中并创建然后序列化的结果树。您似乎认为使用 StAX 会更改 XSLT 的行为,但我认为情况并非如此,XSLT 处理器构建树,因为样式表可能需要复杂的 XPath 导航器,如前置或前置兄弟。

但是,当您使用 Java 时,您可以查看 Saxon 9.3 及其实验性 XSLT 3.0 流式支持,这样您在处理非常大的 XML 输入文档时就不会耗尽内存。

XSLT 中不寻常的部分是<xsl:template match="@k8[parent::point]">,通常简单地编写为,<xsl:template match="point/@k8">但您需要使用 XSLT 处理器测试这是否会改变性能。

于 2011-04-06T15:41:45.260 回答
3

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

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

于 2011-04-06T16:25:57.017 回答
2

转换器读取所有输入,然后开始产生输出(当然,大量流当然经常会在结果之前出现内存不足。

任何的想法?

如果您发现完成这项工作需要很长时间,那么您需要重新设计您的任务方法以避免在开始处理输出文件之前读取整个输入文件。没有什么可以用您的代码进行调整以使其神奇地更快 - 您需要解决算法的核心。

于 2011-04-06T15:24:26.667 回答
1

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

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

巧合的是,最近对另一个问题的回答可能会对您有所帮助。

于 2011-04-06T16:38:44.180 回答
1

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

然后是不同的选择:

于 2011-11-12T21:38:31.127 回答
0

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

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

于 2011-04-06T23:02:37.023 回答