Запись преобразованных XSLT-фрагментов XML в XMLStreamWriter

У меня такая проблема:

  • Большой выходной файл (zip), содержащий один XML-документ ("FeatureCollection").
  • Относительно небольшие фрагменты xml
  • Каждый фрагмент должен быть записан как featureMember в XMLStream после преобразования XSLT.
  • Определения пространств имен только для тега FeatureCollection (корневого).

Теперь я заставил это работать, используя отдельный поток байтов для разбора фрагментов. Я также оборачиваю XMLStream, чтобы избежать открытия / закрытия документа XSLT-преобразователем (Saxon) или закрытия потока.

Однако я считаю, что решение слишком сложное. Должна быть возможность принять контекст JAXB в качестве источника (без промежуточного байтового потока). См. Фрагмент кода:

        try {
            XMLStreamWriterWrapper writer = getWriter( xmlFile );
            for ( Map.Entry<String, String> entry : prefixMapper.getNamespaces().entrySet() ) {
                writer.setPrefix( entry.getValue(), entry.getKey() );
            }

            writer.getWrapperWriter().writeStartDocument();
            writer.writeStartElement( GML_URI, "FeatureCollection" );

            for ( Map.Entry<String, String> entry : prefixMapper.getNamespaces().entrySet() ) {
                writer.getWrapperWriter().writeNamespace( entry.getValue(), entry.getKey() );
            }

            while ( dtoIterator.hasNext() ) {
                writer.writeStartElement( GML_URI, "featureMember" );
                D dto = dtoIterator.next();
                hideAttributes( dto );

                J jaxb = transformToJaxb( dto );

                Source untransformed = new JAXBSource( jaxbContext, getRootElement( jaxb ) );
                getTransformer().transform( untransformed, new StAXResult( writer) );
                writer.writeEndElement();
            }

            writer.writeEndElement();
            writer.getWrapperWriter().writeEndDocument();
            writer.getWrapperWriter().flush();
            writer.getWrapperWriter().close();
        }
        catch ( IOException | JAXBException | TransformerException | XMLStreamException e ) {
            LOG.error( e );
            throw new IllegalArgumentException( e );
        }

private XMLStreamWriterWrapper getWriter( File xmlFile ) throws XMLStreamException, FileNotFoundException, IOException {
    XMLOutputFactory xof = XMLOutputFactory.newFactory();
    xof.setProperty( XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE );

    XMLStreamWriter writer = xof.createXMLStreamWriter( new BufferedOutputStream( new FileOutputStream( xmlFile ) ) );

    return new XMLStreamWriterWrapper( writer );
}           

Ожидаемый результат (от неоптимизированного решения):

<?xml version = "1.0" ?><gml:FeatureCollection xmlns:gml = "http://www.opengis.net/gml/3.2" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink = "http://www.w3.org/1999/xlink" xmlns:brocom = "http://www.broservices.nl/xsd/brocommon/3.0" xmlns:bro = "http://www.pdok.nl/bro">
<gml:featureMember>
    <bro:Characteristics gml:id = "BRO_id_1">
        <brocom:broId>id_1</brocom:broId>
    </bro:Characteristics>
</gml:featureMember>
<gml:featureMember>
    <bro:Characteristics gml:id = "BRO_id_2">
        <brocom:broId>id_2</brocom:broId>
    </bro:Characteristics>
</gml:featureMember>

Однако результат (из фрагмента кода выше):

<?xml version = "1.0" ?><gml:FeatureCollection xmlns:gml = "http://www.opengis.net/gml/3.2" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink = "http://www.w3.org/1999/xlink" xmlns:brocom = "http://www.broservices.nl/xsd/brocommon/3.0" xmlns:bro = "http://www.pdok.nl/bro">
<gml:featureMember>
    <bro:Characteristics gml:id = "BRO_id_1">
        <broId xmlns = "http://www.broservices.nl/xsd/brocommon/3.0">id_1</broId>
    </bro:Characteristics>
</gml:featureMember>
<gml:featureMember>
    <bro:Characteristics gml:id = "BRO_id_2">
        <broId xmlns = "http://www.broservices.nl/xsd/brocommon/3.0">id_2</broId>
    </bro:Characteristics>
</gml:featureMember>

Вопросов:

  1. XMLStreamWriter, похоже, игнорирует свойство IS_REPAIRING_NAMESPACES. Что не так?
  2. Могу ли я оптимизировать трансформер Saxon, чтобы он работал с частичным xml. Ergo: действительно ли мне нужно оборачивать XMLOutputStream, чтобы преобразователь не записывал открывающий / закрывающий документ или вообще не закрывал поток?
  3. Правильно ли я определяю пространства имен (с помощью setPrefix и writeNameSpace).
  4. При использовании маршаллера JAXB я могу установить свойства маршаллера, например: JAXB_FORMATTED_OUTPUT, JAXB_FRAGMENT. Могу ли я сделать это и с этим решением?
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
230
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Обратите внимание, что вы можете использовать саксонскую реализацию XMLStreamWriter вместо той, которую вы используете (Processor.newSerializer().getXMLStreamWriter()). Возможно, это даст вам больше контроля и, возможно, решит проблемы с пространством имен.

Вместо того, чтобы указывать new StaxResult(writer) в качестве второго аргумента transform(), вы можете попробовать указать new net.sf.saxon.stax.ReceiverToXMLStreamWriter(writer), а затем, возможно, создать подкласс ReceiverToXMLStreamWriter, чтобы вызовы startDocument() и endDocument() ничего не делали.

Что касается обработки пространств имен XMLStreamWriter, я боюсь, что спецификация API очень неясна. Я счел полезным проконсультироваться с http://veithen.github.io/2009/11/01/understanding-stax.html, хотя он не имеет официального статуса. Я не могу дать никаких гарантий, что саксонская интерпретация - это то, что задумали авторы API (нет эталонной реализации или набора тестов).

@Mickael Kay: Спасибо за ваш ответ. Саксонская реализация XMLStreamWriter выглядит многообещающей. Использование предложенного ReceiverToXMLStreamWriter дает нулевой указатель: в net.sf.saxon.s9api.XsltTransformer.getDestinationReceiver (Xs‌ ltTransformer.java:6‌ 31)

Sjaak 13.04.2018 17:20

Использование Processor.newSerializer().getXMLStreamWriter() без конфигурации уже устранило проблему. </br> Ссылка действительно была полезной. Теперь я пишу startDocument, startElement и сразу после инициализирую пространства имен с помощью setPrefix и writeNamespace. Автор, кажется, распознает это и больше не добавляет новые определения пространств имен для вложенных элементов. </br> Спасибо.

Sjaak 14.04.2018 11:18

Другие вопросы по теме