Преобразование XML в другой XML с помощью STaX занимает много времени - PullRequest
2 голосов
/ 06 апреля 2011

Я использую следующий код для преобразования большого потока 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 ]

8 голосов
/ 06 апреля 2011

Ну, 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, меняет ли это производительность.

3 голосов
/ 06 апреля 2011

Использование XSLT, вероятно, не лучший подход, так как другие отмечают, что ваше решение требует, чтобы процессор считывал весь документ в память перед записью вывода.Возможно, вы захотите использовать синтаксический анализатор SAX для последовательного чтения в каждом узле, выполнить любое требуемое преобразование (при необходимости используя преобразование данных) и записать преобразованные данные.Это исключает необходимость создания целого дерева документов в памяти и может обеспечить значительно более быструю обработку, поскольку вы не пытаетесь создать сложный документ для записи.

Спросите себя, является ли выходной формат простым и стабильным,а затем пересмотреть использование XSLT.Для больших наборов данных обычных данных вы также можете подумать, является ли XML хорошим форматом файлов для передачи информации.

2 голосов
/ 06 апреля 2011

Преобразователь считывает все входные данные и затем начинает формировать выходные данные (при большом потоке, конечно, очень часто нехватка памяти предшествует результату.

Любая идея?

Если вы обнаружите, что выполнение этой работы занимает слишком много времени, вам необходимо изменить подход к решению задачи, чтобы избежать чтения всего входного файла перед началом обработки выходного файла. Ничто не может быть изменено с вашим кодом, чтобы сделать его волшебным образом быстрее - вам нужно обратиться к ядру вашего алгоритма.

1 голос
/ 13 ноября 2011

Как отмечали другие, использование Stax не изменит способ работы XSLT: он сначала читает все перед началом любой работы. Если вам нужно для работы с очень большими файлами, вам придется использовать что-то отличное от XSLT.

Тогда бывают разные варианты:

1 голос
/ 06 апреля 2011

Насколько сложна трансформация, которую вы делаете с XSL?Можете ли вы сделать то же преобразование, используя только StAX?

С StAX довольно легко написать синтаксический анализатор для соответствия определенному узлу, а затем вставить, изменить или удалить узлы в выходном потоке, в который вы записываете при этомточка.Таким образом, вместо использования XSL для преобразования, вы можете использовать только StAX.Таким образом, вы получаете выгоду от потоковой природы API (не буферизует большое дерево в памяти), и, таким образом, не будет проблем с памятью.

Кстати, этот недавний ответ на другой вопросможет помочь вам в этом.

0 голосов
/ 07 апреля 2011

Попробуйте apache xsltc для лучшей производительности - он использует генерацию кода для простого преобразования.

Ваше XSLt-преобразование выглядит очень просто, как и ваш формат ввода - вы наверняка можете выполнить ручную обработку StAX / SAX и получить действительно хорошее увеличение производительности.

...