Lors du traitement de XML au moyen d'un DOM standard, l'ordre d'attribut n'est pas garanti après la sérialisation. Enfin, c’est ce que je viens de réaliser lors de l’utilisation de l’API standard Java XML Transform pour sérialiser la sortie.
Cependant, je dois garder un ordre. J'aimerais savoir s'il est possible sur Java de conserver l'ordre d'origine des attributs d'un fichier XML traité au moyen d'une API DOM, ou tout autre moyen de forcer l'ordre (éventuellement en utilisant une autre API de sérialisation permettant de définir cette option). type de propriété). Dans mon cas, le traitement réduit la modification de la valeur de certains attributs (pas tous) d’une séquence des mêmes éléments avec un tas d’attributs, et peut-être même l’insertion de quelques éléments supplémentaires.
Existe-t-il un moyen "simple" ou dois-je définir ma propre feuille de style de transformation XSLT pour spécifier la sortie et modifier l'ensemble du fichier XML d'entrée?
Mise à jour Je dois remercier toutes vos réponses. La réponse semble maintenant plus évidente que ce à quoi je m'attendais. Je n'ai jamais prêté attention à l'ordre, car je n'en avais jamais eu besoin auparavant.
La raison principale pour exiger un ordre d'attribut est que le fichier XML résultant est différent. La cible est un fichier de configuration contenant des centaines d'alarmes (chaque alarme est définie par un ensemble d'attributs). Ce fichier a généralement peu de modifications au fil du temps, mais il est pratique de le conserver dans l’ordre car il faut le modifier à la main. De temps en temps, certains projets nécessitent de légères modifications de ce fichier, telles que la définition d'un attribut dans un code spécifique au client.
Je viens de développer une petite application pour fusionner le fichier d'origine (commun à tous les projets) avec des parties spécifiques de chaque projet (modifier la valeur de certains attributs), de sorte que le fichier spécifique au projet reçoive les mises à jour de celui de base (nouvelles définitions d'alarme ou certains attributs). valeurs corrections de bugs). Ma principale motivation pour exiger des attributs ordonnés est de pouvoir vérifier la sortie de l'application à nouveau le fichier d'origine à l'aide d'un outil de comparaison de texte (tel que Winmerge). Si le format (principalement l'attribut order) reste le même, les différences peuvent être facilement repérées.
Je pensais vraiment que c'était possible, car les programmes de traitement XML, tels que XML Spy, vous permettent d'éditer des fichiers XML et d'appliquer des ordres (mode grille). Peut-être que mon seul choix est d’utiliser l’un de ces programmes pour manuellement modifier le fichier de sortie.
Désolé de le dire, mais la réponse est plus subtile que "Non, vous ne pouvez pas" ou "Pourquoi devez-vous le faire en premier lieu?".
La réponse courte est "DOM ne vous permettra pas de faire cela, mais SAX le fera".
Cela est dû au fait que DOM ne se soucie pas de l'ordre des attributs, car cela n'a pas de sens en ce qui concerne la norme, et au moment où le XSL s'empare du flux d'entrée, les informations sont déjà perdues. Conservez gracieusement l'ordre des attributs du flux d'entrée (par exemple Xalan-C (sauf dans un cas) ou Xalan-J (toujours)). Surtout si vous utilisez <xsl:copy*>
.
Les cas où l'ordre d'attribut n'est pas conservé, à ma connaissance, sont . - Si le flux d'entrée est un DOM - Xalan-C: si vous insérez littéralement vos balises d'arborescence de résultats (par exemple <elem att1={@att1} .../>
Voici un exemple avec SAX, pour le compte rendu (inhibition de la DTD persistante également).
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 );
Je voudrais également souligner, à l’intention de nombreux détracteurs, que sont des cas où l’attribut d'ordre importe
Le test de régression est un cas évident… .. Celui qui a été appelé pour optimiser un XSL pas si bien écrit sait que vous voulez généralement vous assurer que les "nouveaux" arbres de résultats sont similaires ou identiques aux "anciens". Et lorsque l'arborescence des résultats est d'environ un million de lignes, les outils de diff XML s'avèrent trop difficiles à manipuler .... __ Dans ces cas, la préservation de l'ordre des attributs est d'une grande aide.
J'espère que cela t'aides ;-)
Regardez la section 3.1 de la recommandation XML. Il dit: "Notez que l'ordre des spécifications d'attribut dans une balise de début ou une balise d'élément vide n'est pas significatif."
Si un logiciel requiert des attributs sur un élément XML pour apparaître dans un ordre spécifique, ce logiciel ne traite pas le XML, il traite un texte qui ressemble superficiellement à XML. Cela doit être corrigé.
S'il ne peut pas être corrigé et que vous devez produire des fichiers conformes à ses exigences, vous ne pouvez pas utiliser de manière fiable les outils XML standard pour produire ces fichiers. Par exemple, vous pouvez essayer (comme vous le suggérez) d’utiliser XSLT pour produire des attributs dans un ordre défini, par exemple:
<test>
<xsl:attribute name="foo"/>
<xsl:attribute name="bar"/>
<xsl:attribute name="baz"/>
</test>
seulement pour constater que le processeur XSLT émet ceci:
<test bar="" baz="" foo=""/>
parce que le DOM que le processeur utilise attribue des ordres à l’ordre alphabétique par nom de balise. (C'est un comportement commun mais pas universel parmi les DOM XML.)
Mais je veux souligner quelque chose. Si un logiciel enfreint la recommandation XML à un égard, il le fait probablement à d’autres égards. Si elle se brise lorsque vous alimentez des attributs dans le mauvais ordre, elle le sera probablement aussi si vous délimitez des attributs avec des guillemets simples ou si les valeurs d'attribut contiennent des entités de caractère, ou n'importe quelle autre douzaine d'éléments mentionnés dans la recommandation XML comme document XML peut faire que l'auteur de ce logiciel n'a probablement pas pensé.
La Canonicalisation XML aboutit à un ordre cohérent d'attributs, principalement pour permettre de vérifier une signature sur tout ou partie du XML, bien qu'il existe d'autres utilisations potentielles. Cela peut convenir à vos objectifs.
Il n'est pas possible de trop insister sur ce que Robert Rossney vient de dire, mais je vais essayer. ;-)
L'avantage des normes internationales est que, lorsque tout le monde les suit, la vie est belle. Tous nos logiciels s'entendent pacifiquement.
XML doit être l’un des standards les plus importants que nous ayons. C'est la base de "vieux Web", comme SOAP, et toujours du "web 2.0", comme RSS et Atom. C'est grâce à des normes claires que XML peut interagir entre différentes plates-formes.
Si nous renonçons peu à peu à XML, nous nous retrouverons dans une situation où un producteur de XML ne pourra pas supposer qu'un consommateur de XML sera capable de consommer son contenu. Cela aurait un effet catastrophique sur l'industrie.
Nous devrions repousser très fort, sur toute personne qui écrit du code qui ne traite pas XML conformément à la norme. Je comprends que, en cette période d’économie, on répugne à offenser les clients et les partenaires commerciaux en disant «non». Mais dans ce cas, je pense que ça vaut le coup. Notre situation financière serait bien pire si nous devions créer à la main du XML pour chaque partenaire.
Donc, n'activez pas les entreprises qui ne comprennent pas XML. Envoyez-leur la norme, avec les lignes appropriées en surbrillance. Ils doivent cesser de penser que XML est simplement un texte contenant des crochets. Il ne se comporte tout simplement pas comme un texte comportant des crochets.
Ce n'est pas comme s'il y avait une excuse pour cela. Même les plus petits appareils intégrés peuvent avoir une implémentation complète de l'analyseur XML. Je n'ai pas encore entendu de bonne raison pour ne pas être en mesure d'analyser le code XML standard, même si on ne peut se permettre une implémentation DOM complète.
Vous ne devriez vraiment pas avoir besoin de garder un ordre quelconque. Autant que je sache, aucun schéma ne prend en compte l'ordre des attributs lors de la validation d'un document XML. Il semble que tout ce qui traite XML à l’autre extrémité n’utilise pas un DOM approprié pour analyser les résultats.
Je suppose qu'une option serait de construire manuellement le document en utilisant la création de chaîne, mais je le déconseille vivement.
J'ai eu exactement le même problème. Je voulais modifier les attributs XML mais je voulais garder l'ordre à cause de diff. J'ai utilisé StAX pour y parvenir. Vous devez utiliser XMLStreamReader et XMLStreamWriter (la solution basée sur le curseur). Lorsque vous obtenez un type d'événement START_ELEMENT, le curseur conserve l'index des attributs. Par conséquent, vous pouvez apporter les modifications appropriées et les écrire dans le fichier de sortie "dans l'ordre".
Regardez cet article/discussion . Vous pouvez voir comment lire les attributs des éléments de départ dans l'ordre.
Robert Rossney l'a bien dit: si vous utilisez l'ordre des attributs, vous ne traitez pas vraiment le XML, mais plutôt quelque chose qui ressemble à XML.
Je peux penser à au moins deux raisons pour lesquelles vous pourriez vous soucier de l'ordre des attributs. Il y en a peut-être d'autres, mais au moins pour ces deux-là, je peux suggérer des alternatives:
Vous utilisez plusieurs instances d'attributs portant le même nom:
<foo myAttribute="a" myAttribute="b" myAttribute="c"/>
C'est tout simplement un XML invalide; un processeur DOM abandonnera probablement toutes ces valeurs sauf une, à condition qu'il traite le document. Au lieu de cela, vous voulez utiliser des éléments enfants:
<foo>
<myChild="a"/>
<myChild="b"/>
<myChild="c"/>
</foo>
Vous supposez qu'une sorte de distinction s'applique aux attributs qui viennent en premier. Rendez ceci explicite, soit par d’autres attributs, soit par des éléments enfants. Par exemple:
<foo attr1="a" attr2="b" attr3="c" theMostImportantAttribute="attr1" />
Vous pouvez toujours le faire en utilisant le DOM standard et l'API de transformation en utilisant une solution rapide et sale comme celle que je décris:
Nous savons que la solution API de transformation ordonne les attributs par ordre alphabétique. Vous pouvez préfixer les noms d'attributs avec des chaînes faciles à supprimer ultérieurement afin qu'elles soient sorties dans l'ordre que vous souhaitez. Des préfixes simples, tels que "a_", "b_", etc., devraient suffire dans la plupart des situations et peuvent facilement être supprimés du fichier xml en sortie à l'aide d'une expression rationnelle one liner.
Si vous chargez un fichier xml et enregistrez à nouveau et souhaitez conserver l'ordre des attributs, vous pouvez utiliser le même principe, en modifiant d'abord les noms des attributs dans le texte xml en entrée, puis en les analysant dans un objet Document. Encore une fois, effectuez cette modification sur la base d'un traitement textuel du code XML. Cela peut être délicat mais peut être fait en détectant des éléments et leurs chaînes d'attributs, encore une fois, en utilisant regex. Notez qu'il s'agit d'une solution sale. L'analyse de XML par vous-même comporte de nombreux pièges, même pour quelque chose d'aussi simple, soyez donc prudent si vous décidez de l'implémenter.
Type de travaux ...
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"));
}
}
}
Dans mon test simple, il imprime:
*** Sort by id ***
1 : 100.00
2 : 5.10
3 : 29.99
*** Sort by price ***
2 : 5.10
3 : 29.99
1 : 100.00
Je pense que je peux trouver des justifications valables pour prendre en compte l'ordre des attributs:
Il semble que la solution d'Alain Pannetier soit la voie à suivre.
Aussi, vous voudrez peut-être jeter un oeil à DecentXML ; cela vous donne un contrôle total sur la façon dont le XML est formaté, même s'il n'est pas compatible DOM. Particulièrement utile si vous souhaitez modifier du code XML édité à la main sans perdre la mise en forme.