Разделить XML поток по XML документам - PullRequest
0 голосов
/ 10 февраля 2020

У меня есть поток бессхемных XML документов, таких как:

<?xml version="1.0" encoding="UTF-8"?>
<message id="1">
    <text>aaaaaaa</text>
</message>
<kuku>
    bbbbb
</kuku>
<?xml version="1.0" encoding="UTF-8"?>
<other_message id="3">
    <text>ccccc</text>
</other_message>

Необходимость разбора документов в потоковом режиме. Решение обернуть поток одним элементом root XML не работает, потому что происходит сбой StAX, когда он встречает элемент <?xml ... ?> внутри документа. Но его можно использовать, если я смогу пропустить этот элемент в потоке ввода.

Все документы могут быть разными, поэтому общего элемента end_document XML нет.

Ответы [ 2 ]

2 голосов
/ 10 февраля 2020

Нет способа сделать это на 100% надежно. Вы не можете сделать это, используя XML синтаксический анализатор, потому что он сообщит об ошибке, когда увидит второе объявление XML, и нет способа исправить эту ошибку. Таким образом, вы должны сделать это, используя свой собственный «предварительный анализ», и всегда есть риск, что ваш предварительный анализ распознает что-то, похожее на объявление XML, но это не так, потому что (например) он находится внутри тело комментария XML или раздела CDATA. Но это, наверное, лучшее, что вы можете сделать. Элегантный способ сделать это, вероятно, написать реализацию InputStream, которая доставляет итерируемую последовательность InputStreams, а затем l oop поверх этой итерируемой передачи, передавая каждый из них в парсер XML по очереди. Как вариант, ваше предложение отфильтровать объявления XML (и добавить внешний начальный тег и конечный тег переноса) также сработает.

Лучше поощрять человека, который предоставил эти данные, что они делают это неправильно.

1 голос
/ 10 февраля 2020

Возможно, но это как-то некрасиво и хаки sh. Вы можете использовать StAX и считать открывающий и закрывающий теги элементов. Увеличьте счетчик на открывающем теге и уменьшите на закрывающем теге. Когда вы достигаете 0, вы знаете, что полностью прочитали элемент root. Используйте метод getLocation() на XMLStreamReader, чтобы увидеть, как далеко вы прочитали, особенно метод getCharsetOffset(). С новой позицией / смещением от вашего исходного источника / потока вы можете построить новый поток с начальной точкой в ​​следующем объявлении XML. В качестве подтверждения концепции см. Следующий код:

String content = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"+
            "<foobar>\n"+
            "    <bla />\n"+
            "</foobar>\n"+
            "<?xml version=\"1.0\" encoding=\"ASCII\" ?>\n"+
            "<first with=\"attributes\">\n"+
            "   <second>\n"+
            "       <third />\n"+
            "   </second>\n"+
            "</first>";
XMLInputFactory factory = XMLInputFactory.newFactory();
InputStream stream = new ByteArrayInputStream(content.getBytes());
XMLStreamReader xmlReader = factory.createXMLStreamReader(stream);
int nestingCounter = 0;
int characterOffset = 0;
while(xmlReader.hasNext()) {
    int event = xmlReader.next();
    characterOffset = xmlReader.getLocation().getCharacterOffset();
    if (event == XMLStreamConstants.START_ELEMENT) {
        nestingCounter++;
    }
    if (event == XMLStreamConstants.END_ELEMENT) {
        nestingCounter--;
    }
    // work with the event/data here
    System.out.println(event);
    if (nestingCounter == 0) {
        break;
    }
}

System.out.println("Second XML");

// build a new stream
content = content.substring(characterOffset).trim();
xmlReader = factory.createXMLStreamReader(new ByteArrayInputStream(content.getBytes()));

// now, again...
while(xmlReader.hasNext()) {
    int event = xmlReader.next();
    if (event == XMLStreamConstants.START_ELEMENT) {
        nestingCounter++;
    }
    if (event == XMLStreamConstants.END_ELEMENT) {
        nestingCounter--;
    }           
    // work with the event/data here
    System.out.println(event);
    if (nestingCounter == 0) {
        break;
    }
}

Это сгенерирует следующий вывод (а не вызовет исключение):

1
4
1
2
4
2
Second XML
1
4
1
4
1
2
4
2
4
2

Очевидно, вы должны использовать правильный l oop и закройте потоки и читателей, это только подтверждение концепции. Кроме того, вы можете столкнуться с проблемами, если у вас есть другие вещи между закрывающим тегом предыдущего элемента root и новым объявлением XML, потому что вы можете иметь этот материал в конце, но не в начале документа XML:

2.1 Правильно оформленные XML Документы

document     ::=      prolog element Misc*

2.8 Пролог и декларация типа документа

Misc         ::=      Comment | PI | S
...