Как разобрать большой XML-документ с помощью DOM?

Я хочу проанализировать элемент xml, который имеет следующие инциденты:

  • и без объявления xml
  • может обслуживать элементы в произвольном порядке
<employees>
   <employee>
      <details>
         <name>Joe</name>
         <age>34</age>
      </details>
      <address>
         <street>test</street>
         <nr>12</nr>
      </address>
   </employee>
   <employee>
      <address>....</address>
      <details>
         <!-- note the changed order of elements! -->
         <age>24</age>
         <name>Sam</name>
      </details>
   </employee>
</employees>

Вывод должен быть в формате CSV:

name;age;street;nr
Joe,34,test,12
Sam,24,...

Проблема: при использовании управляемых событиями парсеров, таких как stax/sax, мне пришлось бы создать временный bean-компонент Employee, свойства которого я установил для каждого узла события, а затем преобразовать bean-компонент в csv.

Но поскольку размер моего xml-файла составляет несколько ГБ, я бы не хотел создавать дополнительные объекты bean-компонентов для каждой записи.

Таким образом, мне, вероятно, придется использовать старый добрый синтаксический анализ DOM? Поправьте, если я ошибаюсь, рад любым предложениям.

Я пробовал следующее. Проблема в том, что doc.getElementsByTagName("employees") возвращает пустой список узлов, в то время как я ожидал бы один элемент xml. Почему?

StringBuilder sb = new StringBuilder();

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(xml)));
doc.getDocumentElement().normalize();

NodeList employees = doc.getElementsByTagName("employees");
for (int i = 0; i < employees.getLength(); i++) {
    Node employee = employees.item(i);
    if (employees.getNodeType() == Node.ELEMENT_NODE) {
        NodeList employee = ((Element) employees).getElementsByTagName("employee");
        for (int j = 0; j < employee.getLength(); j++) {
            NodeList details = ((Element) employee).getElementsByTagName("details");

            //the rest is pseudocode
            for (details)
                sb.append(getElements("name").item(0) + ",");
                sb.append(getElements("age").item(0) + ",");    

            for (address) 
                sb.append(getElements("street").item(0) + ",");
                sb.append(getElements("nr").item(0) + ",");
        }
    }
}
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
72
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Решение DOM будет использовать много памяти, решение SAX / Stax потребует написания и отладки большого количества кода. Идеальный инструмент для этой работы - потоковое преобразование XSLT 3.0:

<xsl:transform version = "3.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
  <xsl:output method = "text"/>
  <xsl:mode streamable = "yes" on-no-match = "shallow-skip"/>
  <xsl:template match = "employee">
    <xsl:value-of select = "copy-of(.)!(.//name, .//age, .//street, .//nr)" 
                  separator = ","/>
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>
</xsl:transform>

ПРИМЕЧАНИЕ

Первоначально я написал выражение select как copy-of(.)//(name, age, street, nr). Это неверно, потому что оператор // сортирует результаты по порядку документов, который нам не нужен. Использование ! и , позволяет избежать сортировки.

Значит, мне нужен только copy-of для сортировки? Потому что select = "//name, //age, //street, //nr" тоже подойдет ...

membersound 17.05.2018 11:43

Без копии код невозможно потоковое. Если вам не нужна потоковая передача, вы можете написать (.//name, .//age, .//street, .//nr). Вам действительно нужен ведущий ». (очевидно).

Michael Kay 17.05.2018 12:24

Не используйте StringBuilder, а сразу пишите в файл (Files.newBufferedWriter).

Разбор XML вручную не представляет большого труда, поскольку, похоже, нет ни высокого уровня сложности, ни необходимости валидации на основе XML.

  • Анализ DOM позволит построить объектную модель документа, чего вы бы не хотели.
  • Stax необходимо создать полноценного сотрудника, если подэлементы неупорядочены.
  • Так что чтение сотрудника самому не будет чем-то особенным.
  • Кроме того, похоже, что XML не возникает из-за записи XML, и, возможно, потребуется исправить недопустимый текст XML, например &, который должен быть &amp; в XML.

Если XML действителен (у вас может быть Reader, который добавляет <?xml ...> впереди), сканирование через XML будет:

XMLInputFactory f = XMLInputFactory.newInstance();
XMLStreamReader r = f.createXMLStreamReader( ... );
while(r.hasNext()) {
    r.next();
}

Это легко позволяет поддерживать карту для атрибутов сотрудников, начиная с <employee> и заканчивая, проверять и записывать на </employee>.

Поэтому мне пришлось бы заранее инициализировать LinkedHashMap с атрибутами, которые я ищу, устанавливать их один за другим, если они обнаруживаются после начального тега?

membersound 08.05.2018 16:59

Да, довольно примитивно и обстоятельно.

Joop Eggen 08.05.2018 17:17

Будет ли это работать лучше, чем подход xslt, описанный ниже, на большом наборе данных?

membersound 08.05.2018 20:51

Я думаю, что это быстрее и требует меньше ресурсов, но стоит ли это гораздо больших усилий по программированию: сначала попробуйте XSLT

Joop Eggen 09.05.2018 09:35

Скорость обоих подходов будет зависеть от стоимости синтаксического анализа XML, которая одинакова в обоих случаях.

Michael Kay 09.05.2018 11:07

@MichaelKay Я поддержал ваш ответ, он должен быть первым, кто попробует. Если тогда это будет медленно, можно подумать о ручном решении.

Joop Eggen 09.05.2018 12:00

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