Я хочу проанализировать элемент 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) + ",");
}
}
}




Решение 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>
</xsl:text>
</xsl:template>
</xsl:transform>
ПРИМЕЧАНИЕ
Первоначально я написал выражение select как copy-of(.)//(name, age, street, nr). Это неверно, потому что оператор // сортирует результаты по порядку документов, который нам не нужен. Использование ! и , позволяет избежать сортировки.
Без копии код невозможно потоковое. Если вам не нужна потоковая передача, вы можете написать (.//name, .//age, .//street, .//nr). Вам действительно нужен ведущий ». (очевидно).
Не используйте StringBuilder, а сразу пишите в файл (Files.newBufferedWriter).
Разбор XML вручную не представляет большого труда, поскольку, похоже, нет ни высокого уровня сложности, ни необходимости валидации на основе XML.
&, который должен быть & в XML.Если XML действителен (у вас может быть Reader, который добавляет <?xml ...> впереди), сканирование через XML будет:
XMLInputFactory f = XMLInputFactory.newInstance();
XMLStreamReader r = f.createXMLStreamReader( ... );
while(r.hasNext()) {
r.next();
}
Это легко позволяет поддерживать карту для атрибутов сотрудников, начиная с <employee> и заканчивая, проверять и записывать на </employee>.
Поэтому мне пришлось бы заранее инициализировать LinkedHashMap с атрибутами, которые я ищу, устанавливать их один за другим, если они обнаруживаются после начального тега?
Да, довольно примитивно и обстоятельно.
Будет ли это работать лучше, чем подход xslt, описанный ниже, на большом наборе данных?
Я думаю, что это быстрее и требует меньше ресурсов, но стоит ли это гораздо больших усилий по программированию: сначала попробуйте XSLT
Скорость обоих подходов будет зависеть от стоимости синтаксического анализа XML, которая одинакова в обоих случаях.
@MichaelKay Я поддержал ваш ответ, он должен быть первым, кто попробует. Если тогда это будет медленно, можно подумать о ручном решении.
Значит, мне нужен только
copy-ofдля сортировки? Потому чтоselect = "//name, //age, //street, //nr"тоже подойдет ...