Порядок атрибутов XML после обработки DOM
При обработке XML с помощью стандартного DOM порядок атрибутов не гарантируется после сериализации обратно. Наконец, это то, что я только что понял, когда использовал стандартный java XML Transform API для сериализации вывода.
Однако мне нужно поддерживать порядок. Я хотел бы знать, есть ли какая-либо возможность на Java сохранить исходный порядок атрибутов XML-файла, обработанного с помощью DOM API, или каким-либо способом принудить порядок (возможно, с помощью альтернативного API сериализации, который позволяет вы устанавливаете этот вид собственности). В моем случае обработка сводится к изменению значения некоторых атрибутов (не всех) последовательности одних и тех же элементов с кучей атрибутов и, возможно, вставке еще нескольких элементов.
Существует ли какой-либо" простой " способ или мне нужно определить свою собственную таблицу стилей преобразования XSLT, чтобы указать выходные данные и изменить весь входной XML-файл?
Обновление я должен поблагодарить все ваши ответы. Ответ теперь кажется более очевидным, чем я ожидал. Я никогда ничего не платил. внимание к порядку атрибутов, поскольку я никогда раньше в нем не нуждался.
Основная причина, по которой требуется порядок атрибутов, заключается в том, что результирующий XML-файл просто выглядит по-другому. Целью является конфигурационный файл, содержащий сотни сигналов тревоги (каждый сигнал тревоги определяется набором атрибутов). Этот файл обычно имеет небольшие изменения с течением времени, но его удобно держать упорядоченным, так как, когда нам нужно что-то изменить, он редактируется вручную. Время от времени некоторые проекты нуждаются в свете модификации этого файла, такие как установка одного из атрибутов для конкретного кода клиента.
Я только что разработал небольшое приложение для объединения исходного файла (общего для всех проектов) с определенными частями каждого проекта (изменение значения некоторых атрибутов), поэтому файл проекта получает обновления базового (новые определения тревоги или исправления некоторых значений атрибутов). Моя основная мотивация требовать упорядоченные атрибуты - это иметь возможность проверить вывод приложения снова и снова. исходный файл с помощью инструмента сравнения текста (например, Winmerge). Если формат (главным образом порядок атрибутов) остается тем же самым, различия можно легко обнаружить.
Я действительно думал, что это возможно, так как программы обработки XML, такие как XML Spy, позволяют редактировать XML-файлы и применять некоторый порядок (режим сетки). Возможно, мой единственный выбор-использовать одну из этих программ для ручного изменения выходного файла.
11 ответов:
Извините, но ответ более тонкий, чем "нет, вы не можете" или "Зачем вам вообще это нужно ?".
Короткий ответ: "дом не позволит вам сделать это, но Сакс позволит".Это происходит потому, что DOM не заботится о порядке атрибутов, поскольку это бессмысленно с точки зрения стандарта, и к тому времени, когда XSL получает входной поток, информация уже теряется. Большинство движков XSL фактически изящно сохраняют атрибут входного потока заказа (например, Xalan-C (за исключением одного случая) или Xalan-J (всегда)). Особенно если вы используете
<xsl:copy*>.Случаи, когда порядок атрибутов не соблюдается, насколько мне известно, есть. - Если входной поток является DOM - Xalan-C: если вы вставляете теги дерева результатов буквально (например,
<elem att1={@att1} .../>Вот один пример с SAX, для записи (ингибирование DTD ворчание, а также).
SAXParserFactory spf = SAXParserFactoryImpl.newInstance(); spf.setNamespaceAware(true); spf.setValidating(false); spf.setFeature("http://xml.org/sax/features/validation", false); spf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); SAXParser sp = spf.newSAXParser() ; Source src = new SAXSource ( sp.getXMLReader(), new InputSource( input.getAbsolutePath() ) ) ; String resultFileName = input.getAbsolutePath().replaceAll(".xml$", ".cooked.xml" ) ; Result result = new StreamResult( new File (resultFileName) ) ; TransformerFactory tf = TransformerFactory.newInstance(); Source xsltSource = new StreamSource( new File ( COOKER_XSL ) ); xsl = tf.newTransformer( xsltSource ) ; xsl.setParameter( "srcDocumentName", input.getName() ) ; xsl.setParameter( "srcDocumentPath", input.getAbsolutePath() ) ; xsl.transform(src, result );Я также хотел бы указать на намерение многих скептиков, что есть случаи, когда порядок атрибутов не имеет значения.
Регрессионное тестирование-очевидный случай. Тот, кто был вызван для оптимизации не очень хорошо написанного XSL, знает, что вы обычно хотите убедиться, что "новые" результирующие деревья похожи или идентичны "старым". А когда результирующее дерево составляет около миллиона строк, инструменты XML diff оказываются слишком громоздкими... В этих случаях сохранение порядка атрибутов очень помогает.Надеюсь, это поможет ; -)
Посмотрите на раздел 3.1 рекомендации XML. Он говорит: "Обратите внимание, что порядок спецификаций атрибутов в теге start-tag или empty-element не имеет значения."
Если часть программного обеспечения требует, чтобы атрибуты элемента XML отображались в определенном порядке, то это программное обеспечение не обрабатывает XML, а обрабатывает текст, который внешне выглядит как XML. Это нужно исправить.
Если он не может быть исправлен, и вы должны создать файлы, соответствующие его требованиям, вы не можете надежно используйте стандартные инструменты XML для создания этих файлов. Например, вы можете попробовать (как вы предлагаете) использовать XSLT для создания атрибутов в определенном порядке, например:
<test> <xsl:attribute name="foo"/> <xsl:attribute name="bar"/> <xsl:attribute name="baz"/> </test>Только для того, чтобы обнаружить, что процессор XSLT выдает следующее:
<test bar="" baz="" foo=""/>Потому что DOM, который использует процессор, упорядочивает атрибуты в алфавитном порядке по имени тега. (Это обычное, но не универсальное поведение среди XML-доменов.)
, но я хочу кое-что подчеркнуть. Если часть программного обеспечения нарушает рекомендацию XML в одном отношении он, вероятно, нарушает его в других отношениях. Если он ломается, когда вы передаете ему атрибуты в неправильном порядке, он, вероятно, также ломается, если вы разделяете атрибуты одинарными кавычками, или если значения атрибутов содержат символьные сущности, или любую из дюжины других вещей, о которых рекомендация XML говорит, что XML-документ может делать то, о чем автор этого программного обеспечения, вероятно, не думал.
Канонизация XML приводит к последовательному упорядочению атрибутов, в первую очередь для того, чтобы можно было проверить подпись над некоторыми или всеми XML, хотя есть и другие потенциальные применения. Это может соответствовать вашим целям.
Невозможно переоценить то, что только что сказал Роберт Россни, но я попытаюсь. ;- )
Преимущество международных стандартов состоит в том, что, когда все следуют им, жизнь хороша. Все наши программы мирно уживаются. XML должен быть одним из самых важных стандартов, которые у нас есть. Это основа" старого веба", такого как SOAP, и все еще "Веб 2.0", таких как RSS и Atom. Именно из-за четких стандартов XML способен взаимодействовать между различными платформы.Если мы постепенно откажемся от XML, мы попадем в ситуацию, когда производитель XML не сможет предположить, что потребитель XML сможет потреблять их содержимое. Это будет иметь катастрофические последствия для отрасли.
Мы должны очень сильно давить на всех, кто пишет код, который не обрабатывает XML в соответствии со стандартом. Я понимаю, что в наше экономическое время есть нежелание обижать клиентов и деловых партнеров тем, что сказать"нет". Но в данном случае, я думаю, оно того стоит. Мы были бы в гораздо худшем финансовом положении, если бы нам пришлось вручную создавать XML для каждого делового партнера.
Поэтому не "включайте" компании, которые не понимают XML. Отправьте им стандарт,выделив соответствующие строки. Им нужно перестать думать, что XML - это просто текст с угловыми скобками в нем. Он просто не ведет себя как текст с угловыми скобками в нем.
Этому нет оправдания. Даже самый маленький встроенные устройства могут иметь полнофункциональные реализации синтаксического анализа XML. Я еще не слышал веской причины для того, чтобы не иметь возможности анализировать стандартный XML, даже если вы не можете позволить себе полнофункциональную реализацию DOM.
Вам действительно не нужно поддерживать какой-либо порядок. Насколько мне известно, ни одна схема не учитывает порядок атрибутов при проверке XML-документа. Похоже, что то, что обрабатывает XML на другом конце, не использует правильный DOM для анализа результатов.
Я полагаю, что одним из вариантов было бы вручную построить документ с помощью string building, но я настоятельно рекомендую этого не делать.
Роберт Россни хорошо сказал: Если вы полагаетесь на упорядочение атрибутов, вы на самом деле обрабатываете не XML, а что-то, что выглядит как XML.
Я могу придумать по крайней мере две причины, по которым вы могли бы заботиться о порядке атрибутов. Могут быть и другие, но по крайней мере для этих двух я могу предложить альтернативы:
Вы используете несколько экземпляров атрибутов с одинаковым именем:
<foo myAttribute="a" myAttribute="b" myAttribute="c"/>Это просто недопустимый XML; процессор DOM, вероятно, будет отбросьте все эти значения, кроме одного-если он вообще обрабатывает документ. Вместо этого вы хотите использовать дочерние элементы:
<foo> <myChild="a"/> <myChild="b"/> <myChild="c"/> </foo>Вы предполагаете, что какое-то различие применяется к атрибутам, которые приходят первыми. Сделайте это явным, либо через другие атрибуты, либо через дочерние элементы. Например:
<foo attr1="a" attr2="b" attr3="c" theMostImportantAttribute="attr1" />
У меня была точно такая же проблема. Я хотел изменить атрибуты XML, но хотел сохранить порядок из-за различий. Для этого я использовал StAX. Вы должны использовать XMLStreamReader и XMLStreamWriter (решение на основе Курсора). Когда вы получаете тип события START_ELEMENT, курсор сохраняет индекс атрибутов. Следовательно, вы можете внести соответствующие изменения и записать их в выходной файл "по порядку".
Посмотрите на этустатью/дискуссию . Вы можете видеть, как читать атрибуты стартовых элементов по порядку.
Вы все еще можете сделать это с помощью стандартного DOM и Transformation API, используя быстрое и грязное решение, подобное тому, которое я описываю:
Мы знаем, что решение transformation API упорядочивает атрибуты в алфавитном порядке. Вы можете префиксировать имена атрибутов с помощью простых в обработке строк, чтобы они выводились в нужном порядке. Простые префиксы типа "a_", " b_ " и т. д. должны быть достаточными в большинстве ситуаций и могут быть легко удалены из выходного xml с помощью одного лайнера регулярное выражение.
Если вы загружаете xml и повторно сохраняете и хотите сохранить порядок атрибутов, вы можете использовать тот же принцип, сначала изменив имена атрибутов во входном xml-тексте, а затем проанализировав его в объект документа. Опять же, сделайте это изменение на основе текстовой обработки xml. Это может быть сложно, но может быть сделано путем обнаружения элементов и их атрибутов строк, опять же, с помощью регулярных выражений. Обратите внимание, что это грязное решение. Есть много подводных камней при разборе XML на вашем компьютере. даже для такой простой вещи, как эта, поэтому будьте осторожны, если вы решите реализовать это.
Вид работ...
package mynewpackage; // for the method import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; // for the test example import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.StringReader; import org.w3c.dom.Document; import java.math.BigDecimal; public class NodeTools { /** * Method sorts any NodeList by provided attribute. * @param nl NodeList to sort * @param attributeName attribute name to use * @param asc true - ascending, false - descending * @param B class must implement Comparable and have Constructor(String) - e.g. Integer.class , BigDecimal.class etc * @return */ public static Node[] sortNodes(NodeList nl, String attributeName, boolean asc, Class<? extends Comparable> B) { class NodeComparator<T> implements Comparator<T> { @Override public int compare(T a, T b) { int ret; Comparable bda = null, bdb = null; try{ Constructor bc = B.getDeclaredConstructor(String.class); bda = (Comparable)bc.newInstance(((Element)a).getAttribute(attributeName)); bdb = (Comparable)bc.newInstance(((Element)b).getAttribute(attributeName)); } catch(Exception e) { return 0; // yes, ugly, i know :) } ret = bda.compareTo(bdb); return asc ? ret : -ret; } } List<Node> x = new ArrayList<>(); for(int i = 0; i < nl.getLength(); i++) { x.add(nl.item(i)); } Node[] ret = new Node[x.size()]; ret = x.toArray(ret); Arrays.sort(ret, new NodeComparator<Node>()); return ret; } public static void main(String... args) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; String s = "<xml><item id=\"1\" price=\"100.00\" /><item id=\"3\" price=\"29.99\" /><item id=\"2\" price=\"5.10\" /></xml>"; Document doc = null; try { builder = factory.newDocumentBuilder(); doc = builder.parse(new InputSource(new StringReader(s))); } catch(Exception e) { System.out.println("Alarm "+e); return; } System.out.println("*** Sort by id ***"); Node[] ret = NodeTools.sortNodes(doc.getElementsByTagName("item"), "id", true, Integer.class); for(Node n: ret) { System.out.println(((Element)n).getAttribute("id")+" : "+((Element)n).getAttribute("price")); } System.out.println("*** Sort by price ***"); ret = NodeTools.sortNodes(doc.getElementsByTagName("item"), "price", true, BigDecimal.class); for(Node n: ret) { System.out.println(((Element)n).getAttribute("id")+" : "+((Element)n).getAttribute("price")); } } }В моем простом тесте он выводит:
*** Sort by id *** 1 : 100.00 2 : 5.10 3 : 29.99 *** Sort by price *** 2 : 5.10 3 : 29.99 1 : 100.00
Я думаю, что могу найти некоторые обоснования для заботы о порядке атрибутов:
- Возможно, вы ожидаете, что людям придется вручную читать, диагностировать или редактировать данные XML в тот или иной момент; в этом случае будет важна читабельность, а последовательное и логическое упорядочение атрибутов поможет в этом;
- Возможно, вам придется связаться с каким-то инструментом или сервисом, который (предположительно ошибочно) заботится о заказе; просить поставщика исправить его код может не быть один из вариантов: попробуйте спросить об этом у правительственного учреждения, в то время как крайний срок для электронной доставки пакета финансовых документов для вашего пользователя становится все ближе и ближе!
Похоже, что решение Алена Паннетье - это путь.
Кроме того, вы можете взглянуть на DecentXML; это дает вам полный контроль над тем, как форматируется XML, даже если он не совместим с DOM. Особенно полезно, если вы хотите изменить некоторые вручную отредактированные XML без потери форматирование.
У меня есть очень похожая проблема. Мне нужно всегда иметь один и тот же атрибут для первого. Пример:
<h50row a="1" xidx="1" c="1"></h50row> <h50row a="2" b="2" xidx="2"></h50row>Должен стать
<h50row xidx="1" a="1" c="1"></h50row> <h50row xidx="2" a="2" b="2"></h50row>Я нашел решение с регулярным выражением:
test = "<h50row a=\"1\" xidx=\"1\" c=\"1\"></h50row>"; test = test.replaceAll("(<h5.*row)(.*)(.xidx=\"\\w*\")([^>]*)(>)", "$1$3$2$4$5");Надеюсь, вы найдете это полезным
Comments