Подтвердить что ты не робот

Порядок атрибутов XML после обработки DOM

При обработке XML с помощью стандартного DOM порядок атрибутов не гарантируется после сериализации. Наконец, это то, что я только что понял при использовании стандартного java XML Transform API для сериализации вывода.

Однако мне нужно соблюдать порядок. Я хотел бы знать, есть ли какая-либо возможность на Java сохранять исходный порядок атрибутов XML файла, обработанного с помощью DOM API, или любой способ заставить порядок (возможно, используя альтернативный API-интерфейс сериализации, который позволяет вам установить этот вид собственности). В моем случае обработка уменьшает, чтобы изменить значение некоторых атрибутов (не всех) последовательности тех же элементов с кучей атрибутов и, возможно, вставить еще несколько элементов.

Есть ли какой-либо "простой" способ или мне нужно определить мою собственную таблицу стилей преобразования XSLT, чтобы указать вывод и изменить весь входной файл XML?

Обновить Я должен поблагодарить все ваши ответы. Ответ кажется более очевидным, чем я ожидал. Я никогда не обращал внимания на порядок атрибутов, так как раньше я никогда не нуждался.

Основная причина, по которой требуется порядок атрибутов, заключается в том, что полученный XML файл выглядит иначе. Цель - это файл конфигурации, содержащий сотни сигналов тревоги (каждый сигнал тревоги определяется набором атрибутов). Этот файл обычно имеет небольшие модификации с течением времени, но его удобно хранить, поскольку, когда нам нужно что-то изменить, он редактируется вручную. Время от времени некоторые проекты нуждаются в легких модификациях этого файла, например, при установке одного из атрибутов на конкретный клиентский код.

Я только что разработал небольшое приложение для объединения исходного файла (общего для всех проектов) с конкретными частями каждого проекта (измените значение некоторых атрибутов), поэтому файл, зависящий от проекта, получает обновления базового (новые определения сигналов или некоторые исправления атрибутов). Моей главной мотивацией требовать упорядоченных атрибутов является возможность проверить вывод приложения снова на исходный файл с помощью инструмента сравнения текста (например, Winmerge). Если формат (в основном порядок атрибутов) остается неизменным, различия могут быть легко обнаружены.

Я действительно думал, что это возможно, поскольку программы обработки XML, такие как XML Spy, позволяют редактировать файлы XML и применять некоторые упорядочения (режим сетки). Возможно, мой единственный выбор - использовать одну из этих программ для ручной модификации выходного файла.

4b9b3361

Ответ 1

Извините, но ответ более тонкий, чем "Нет, вы не можете" или "Зачем вам это нужно в первую очередь?".

Короткий ответ: "DOM не позволит вам это сделать, но SAX будет".

Это связано с тем, что 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 выглядят слишком громоздкими... В этих случаях сохранение порядка атрибутов очень помогает.

Надеюсь, что это поможет; -)

Ответ 2

Посмотрите раздел 3.1 рекомендации XML. В нем говорится: "Обратите внимание, что порядок спецификаций атрибутов в теге start-tag или теге элемента невелик".

Если часть программного обеспечения требует, чтобы атрибуты на элементе 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 DOM.)

Но я хочу подчеркнуть что-то. Если часть программного обеспечения нарушает рекомендацию XML в одном отношении, она, вероятно, нарушает ее в других отношениях. Если он ломается, когда вы кормите его атрибутами в неправильном порядке, он, вероятно, также ломается, если вы делите атрибуты с одинарными кавычками или если значения атрибута содержат объекты символов или любую из десятков других вещей, которые в рекомендации XML говорят о том, что XML-документ может сделать, что автор этого программного обеспечения, вероятно, не думал.

Ответ 3

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

Ответ 4

Невозможно переоценить то, что сказал Роберт Россни, но я попробую.; -)

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

XML должен быть одним из самых важных стандартов, которые у нас есть. Это основа "старой сети", такой как SOAP, и все еще "веб-2.0", вроде RSS и Atom. Это из-за четких стандартов, что XML может взаимодействовать между различными платформами.

Если мы постепенно откажемся от XML, мы столкнемся с ситуацией, когда производитель XML не сможет предположить, что потребитель XML сможет использовать свой контент. Это может оказать катастрофическое воздействие на отрасль.

Мы должны оттолкнуться очень сильно, на всех, кто пишет код, который не обрабатывает XML в соответствии со стандартом. Я понимаю, что в эти экономические времена существует нежелание оскорблять клиентов и деловых партнеров, говоря "нет". Но в этом случае, я думаю, это того стоит. Мы были бы в гораздо худшем финансовом состоянии, если бы нам пришлось обрабатывать XML для каждого делового партнера.

Таким образом, не "разрешайте" компании, которые не понимают XML. Отправьте им стандарт, с выделенными линиями. Им нужно перестать думать, что XML - это просто текст с угловыми скобками в нем. Он просто не ведет себя как текст с угловыми скобками в нем.

Не похоже, что это оправдание. Даже самые маленькие встроенные устройства могут иметь полнофункциональные реализации парсера XML в них. Я еще не слышал хорошей причины не разбираться в стандартном XML, даже если вы не можете позволить себе полнофункциональную реализацию DOM.

Ответ 5

Вам действительно не нужно сохранять какой-либо порядок. Насколько я знаю, никакая схема не учитывает порядок атрибутов при проверке XML-документа. Похоже, что обработка XML на другом конце не использует правильный DOM для анализа результатов.

Я полагаю, что одним из вариантов было бы вручную создать документ, используя строковое построение, но я настоятельно рекомендую против этого.

Ответ 6

Роберт Россни сказал это хорошо: если вы полагаетесь на упорядочение атрибутов, вы на самом деле не обрабатываете 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" />
    

Ответ 7

У меня была такая же точная проблема. Я хотел изменить атрибуты XML, но хотел сохранить порядок из-за diff. Для этого я использовал StAX. Вы должны использовать XMLStreamReader и XMLStreamWriter (решение на основе курсора). Когда вы получаете тип события START_ELEMENT, курсор сохраняет индекс атрибутов. Следовательно, вы можете внести соответствующие изменения и записать их в выходной файл "по порядку".

Посмотрите на статью/обсуждение. Вы можете увидеть, как читать атрибуты начальных элементов по порядку.

Ответ 8

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

  • Вы можете ожидать, что людям придется вручную читать, диагностировать или редактировать данные XML в тот или иной момент; в этом случае важна читаемость, и с этим помогает последовательный и логичный порядок атрибутов;
  • Возможно, вам придется общаться с каким-то инструментом или услугой, которые (с признательностью ошибочно) заботятся о заказе; просить провайдера исправить свой код, возможно, не вариант: попробуйте спросить об этом у правительственного агентства, пока ваш пользовательский срок для электронной доставки кучу фискальных документов становится все ближе и ближе!

Кажется, что решение Алена Паннетьера - это путь.

Кроме того, вы можете взглянуть на DecentXML; он дает вам полный контроль над тем, как отформатирован XML, даже если он не совместим с DOM. Особенно полезно, если вы хотите изменить отредактированный вручную XML без потери форматирования.

Ответ 9

Вы все еще можете сделать это, используя стандартный DOM и Transformation API, используя быстрое и грязное решение, подобное тому, которое я описываю:

Мы знаем, что решение API преобразования заказывает атрибуты в алфавитном порядке. Вы можете префикс имен атрибутов с помощью нескольких строк, упрощающих запись, чтобы они выводились в том порядке, в котором вы хотите. Простые префиксы как "a_" "b_" и т.д. Должны быть достаточными в большинстве ситуаций и могут быть легко удалены из выходного xml с использованием одного линейного регулярного выражения.

Если вы загружаете xml и сохраняете и хотите сохранить порядок атрибутов, вы можете использовать тот же принцип, сначала изменив имена атрибутов во входном XML-тексте, а затем разобрав его в объект Document. Снова сделайте эту модификацию на основе текстовой обработки xml. Это может быть сложно, но может быть сделано путем обнаружения элементов и строк их атрибутов, опять же, с использованием регулярных выражений. Обратите внимание, что это грязное решение. Есть много ошибок при анализе XML по своему усмотрению, даже для чего-то такого же простого, так что будьте осторожны, если вы решите реализовать это.

Ответ 10

Виды работ...

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

Ответ 11

У меня довольно похожая проблема. Мне нужно всегда иметь тот же атрибут для первого. Пример:

<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");

Надеюсь, вы найдете это полезное