it-swarm.dev

Pořadí XML atributů po zpracování DOM

Při zpracování XML pomocí standardního DOM není po zadání serializace zaručeno pořadí atributů. Konečně to je to, co jsem si právě uvědomil, když používám standardní Java XML Transform API pro serializaci výstupu.

Musím si však udržet pořádek. Chtěl bych vědět, zda existuje nějaká možnost na Java udržet původní pořadí atributů XML souboru zpracovaného pomocí DOM API, nebo jakýmkoliv způsobem vynutit objednávku (možná použitím alternativního serializačního API, které vám umožní nastavit toto rozhraní). druh majetku). V mém případě zpracování zmenšuje hodnotu některých atributů (ne všech) posloupnosti stejných prvků se spoustou atributů a možná vloží několik dalších prvků.

Existuje nějaký "snadný" způsob, nebo musím definovat svůj vlastní XSLT transformační styl pro určení výstupu a změnu celého vstupního XML souboru?

Update Musím poděkovat všem vašim odpovědím. Odpověď se zdá být jasnější, než jsem očekával. Nikdy jsem nevěnoval pozornost atributu řádu, protože jsem to nikdy předtím nepotřeboval.

Hlavním důvodem pro to, aby se objednávka atributů vyžadovala, je to, že výsledný soubor XML se liší pouze vypadá. Cíl je konfigurační soubor, který obsahuje stovky alarmů (každý alarm je definován množinou atributů). Tento soubor má obvykle jen malé změny v čase, ale je vhodné si ho nechat objednat, protože když potřebujeme něco změnit, je ručně upravováno. Nyní a pak některé projekty potřebují lehké úpravy tohoto souboru, například nastavení jednoho z atributů na kód specifický pro zákazníka. 

Právě jsem vytvořil malou aplikaci, která sloučí původní soubor (společný pro všechny projekty) se specifickými částmi každého projektu (změnou hodnoty některých atributů), takže soubor specifický pro projekt dostane aktualizace základní (nové definice alarmů nebo nějaký atribut) hodnoty). Hlavní motivací k tomu, abych si objednané atributy vyžádal, je schopnost kontrolovat výstup aplikace proti původnímu souboru pomocí textového srovnávacího nástroje (např. Winmerge). Pokud formát (hlavně pořadí atributů) zůstává stejný, rozdíly lze snadno zjistit.

Opravdu jsem si myslel, že je to možné, protože programy pro zpracování XML, jako je XML Spy, umožňují upravovat soubory XML a aplikovat některé objednávky (režim mřížky). Možná, že má jediná volba je použít jeden z těchto programů na ručně upravit výstupní soubor.

38

Je mi líto, že řeknu, ale odpověď je jemnější než "Ne, nemůžete" nebo "Proč to musíte udělat na prvním místě?".

Krátká odpověď zní: "DOM vám to nedovolí, ale SAX bude".

Důvodem je, že DOM se nestará o pořadí atributů, protože je to bezvýznamné, co se týče standardu, a v době, kdy se XSL zmocní vstupního proudu, je informace již ztracena. Většina XSL engine bude ve skutečnosti ladně zachovat pořadí atributů vstupního toku (např. [.]. Xalan-C (kromě jednoho případu) nebo Xalan-J (vždy)). Zvláště pokud používáte <xsl:copy*>.

Případy, ve kterých není zachováno pořadí atributů, jsou podle mého nejlepšího vědomí. - Pokud je vstupní tok DOM - Xalan-C: pokud doslova vložíte tagy výsledků (např. <elem att1={@att1} .../>

Zde je jeden příklad s SAX, pro záznam (inhibice DTD nagging stejně).

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

Chtěl bych také poukázat na úmysl mnoha naysayers, že tam jsou případech, kde atribut pořadí dělá záležitost. 

Regresní testování je samozřejmým případem. Kdo byl povolán k optimalizaci ne-tak dobře napsaného XSL, ví, že se obvykle chcete ujistit, že "nové" výsledné stromy jsou podobné nebo stejné jako ty "staré". A když je výsledkový strom kolem jednoho milionu řádků, nástroje XML diff se ukáží jako příliš nepružné ... V těchto případech pomáhá zachovat pořadí atributů.

Snad to pomůže ;-)

22
Alain Pannetier

Podívejte se na část 3.1 doporučení XML. Říká: "Všimněte si, že pořadí specifikací atributů v tagu start-tag nebo prázdném prvku není významné."

Pokud některý software vyžaduje, aby se atribut na elementu XML objevil v určitém pořadí, tento software nespracovává XML, je to zpracování textu, který vypadá povrchně jako XML. Je třeba ji opravit. 

Pokud to nelze opravit a vy musíte vytvořit soubory, které vyhovují jeho požadavkům, nemůžete spolehlivě používat standardní XML nástroje pro tvorbu těchto souborů. Můžete například zkusit (jak navrhujete) použít XSLT k vytvoření atributů v definovaném pořadí, např .:

<test>
   <xsl:attribute name="foo"/>
   <xsl:attribute name="bar"/>
   <xsl:attribute name="baz"/>
</test>

pouze aby zjistil, že procesor XSLT vydává toto:

<test bar="" baz="" foo=""/>

protože DOM, že procesor používá příkazy abecedně podle názvu značky. (To je běžné, ale ne univerzální chování mezi XML DOMy.)

Ale chci něco zdůraznit. Pokud kus softwaru porušuje doporučení XML v jednom ohledu, pravděpodobně ho porušuje v jiných ohledech. Pokud se zlomí, když je krmíte atributy ve špatném pořadí, pravděpodobně se také rozbije, pokud oddělíte atributy s jednoduchými uvozovkami, nebo pokud hodnoty atributů obsahují entity znaků, nebo některý z tucet dalších věcí, které doporučení XML říká, že dokument XML může udělat, že autor tohoto softwaru asi nemyslel.

24
Robert Rossney

XML Canonicalisation má za následek konzistentní uspořádání atributů, především proto, aby umožnil kontrolovat podpis nad některými nebo všemi XML, i když existují jiná potenciální použití. To může vyhovovat vašim účelům.

9
Jon Hanna

Není možné přece zdůraznit, co právě řekl Robert Rossney, ale zkusím to. ;-)

Přínosem mezinárodních norem je, že když je každý sleduje, život je dobrý. Veškerý náš software se vynáší v klidu.

XML musí být jedním z nejdůležitějších standardů, které máme. Je to základ "staré webové" věci, jako je SOAP, a stále 'web 2.0' věci, jako je RSS a Atom. Je to díky jasným standardům, které XML dokáže spolupracovat mezi různými platformami.

Pokud se vzdáme XML, postupně se dostaneme do situace, kdy výrobce XML nebude schopen předpokládat, že spotřebitel XML bude schopen spotřebovat jejich obsah. To by mělo katastrofální dopad na průmysl.

Měli bychom tlačit velmi silně, na každého, kdo píše kód, který nezpracovává XML podle standardu. Chápu, že v těchto hospodářských časech je neochota urazit zákazníky a obchodní partnery tím, že řekne „ne“. Ale v tomto případě si myslím, že to stojí za to. Byli bychom v mnohem horším finančním stavu, kdybychom museli pro každého obchodního partnera předat řemeslo XML.

Takže "nedovolte" společnostem, které nerozumí XML. Pošlete jim standard, s příslušnými řádky zvýrazněny. Musí přestat myslet, že XML je jen text s úhlovými závorkami v něm. Jednoduše se ne chová jako text s úhlovými závorkami v něm.

Není to tak, že by pro to byla omluva. Dokonce i nejmenší vložená zařízení mohou mít plnohodnotné implementace XML parserů. Ještě jsem neslyšel dobrý důvod pro to, že jsem nebyl schopen analyzovat standardní XML, i když si člověk nemůže dovolit plnohodnotnou implementaci DOM.

7
John Saunders

Opravdu byste neměli potřebovat udržovat jakýkoliv druh řádu. Pokud vím, žádné schéma nebere při ověřování dokumentu XML v úvahu pořadí atributů. Zní to jako cokoliv, co zpracovává XML na druhém konci, nepoužívá vlastní DOM k analýze výsledků.

Předpokládám, že jednou možností by bylo manuálně sestavit dokument pomocí budování řetězců, ale důrazně doporučuji proti němu.

2
Soviut

Měla jsem stejný přesný problém. Chtěl jsem modifikovat atributy XML, ale chtěl jsem zachovat pořadí kvůli rozdílu. K dosažení tohoto cíle jsem použil StAX . Musíte použít XMLStreamReader a XMLStreamWriter (řešení založené na kurzorech). Když dostanete typ události START_ELEMENT, kurzor udrží index atributů. Proto můžete provést příslušné úpravy a zapsat je do výstupního souboru "v pořádku". 

Podívejte se na toto článek/diskusi . Můžete vidět, jak číst atributy startovacích prvků v pořadí. 

1
Bashir

Robert Rossney to řekl dobře: pokud se spoléháte na uspořádání atributů, ve skutečnosti nezpracováváte XML, ale spíše něco, co vypadá jako XML.

Můžu si představit alespoň dva důvody, proč byste se mohli zajímat o uspořádání atributů. Mohou existovat i další, ale alespoň pro tyto dva mohu navrhnout alternativy:

  1. Používáte více instancí atributů se stejným názvem:

    <foo myAttribute="a" myAttribute="b" myAttribute="c"/>
    

    Toto je prostě prostý neplatný XML; Procesor DOM pravděpodobně všechny tyto hodnoty zruší - pokud dokument vůbec zpracovává. Místo toho chcete použít podřízené prvky:

    <foo>
        <myChild="a"/>
        <myChild="b"/>
        <myChild="c"/>
    </foo>
    
  2. Předpokládáte, že se na atribut (y), které přicházejí jako první, vztahuje nějaký rozdíl. Toto proveďte explicitně prostřednictvím jiných atributů nebo prostřednictvím podřízených prvků. Například:

    <foo attr1="a" attr2="b" attr3="c" theMostImportantAttribute="attr1" />
    
1
Dan Breslau

Můžete tak učinit pomocí standardního rozhraní DOM a Transformation API pomocí rychlého a špinavého řešení, které popisuji:

Víme, že řešení transformace API objednává atributy abecedně. Názvy atributů můžete předponovat některými strunovými řetězci, které se budou později odstraňovat, takže budou v požadovaném pořadí. Jednoduché předpony jako "a_" "b_" atd. By měly ve většině situací stačit a lze je snadno odstranit z výstupního XML pomocí jednoho linerového regexu.

Pokud načítáte xml a uložíte a chcete zachovat pořadí atributů, můžete použít stejný princip, nejprve změňte názvy atributů ve vstupním xml textu a poté je analyzovat do objektu dokumentu. Tuto změnu opět proveďte na základě textového zpracování XML. To může být složité, ale může být provedeno detekcí prvků a jejich atributových řetězců, opět pomocí regexu. Všimněte si, že se jedná o špinavý roztok. Existuje mnoho úskalí při analýze XML na vlastní pěst, a to i pro něco tak jednoduchého, jako je tento, takže buďte opatrní, pokud se rozhodnete provést.

0
Radu Simionescu

Druh prací ...

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

V mém jednoduchém testu vytiskne:

*** Sort by id ***
1 : 100.00
2 : 5.10
3 : 29.99
*** Sort by price ***
2 : 5.10
3 : 29.99
1 : 100.00
0
Andrey Lebedenko

Myslím, že mohu najít některá platná odůvodnění pro péči o atribut pořadí: 

  • Možná budete očekávat, že lidé budou muset manuálně číst, diagnostikovat nebo upravovat data XML jednou nebo později; v tomto případě bude důležitá čitelnost a s tím bude konzistentní a logické uspořádání atributů;
  • Možná budete muset komunikovat s nějakým nástrojem nebo službou, která (obdivně chybně) se stará o objednávku; požádat poskytovatele, aby opravil svůj kód, nemusí být volbou: zkuste se zeptat od vládní agentury, zatímco lhůta vašeho uživatele pro elektronickou dodávku spoustu fiskálních dokumentů se blíží a blíží!

Vypadá to, že řešení Alaina Pannetiera je cesta.

Také se můžete podívat na DecentXML ; dává vám plnou kontrolu nad formátováním XML, i když to není kompatibilní s DOM. Zvláště užitečné, pokud chcete změnit některé ručně editované XML bez ztráty formátování.

0
Haroldo_OK