2009-04-07 20 views
33

Al procesar XML mediante DOM estándar, no se garantiza el orden de los atributos después de serializar de nuevo. Por último, eso es lo que acabo de ver cuando uso la API java XML Transform estándar para serializar el resultado.Orden de los atributos XML después del procesamiento DOM

Sin embargo, debo mantener un orden. Me gustaría saber si hay alguna posibilidad en Java de mantener el orden original de los atributos de un archivo XML procesado por DOM API, o cualquier forma de forzar el orden (tal vez usando una API de serialización alternativa que te permita configurar esto tipo de propiedad). En mi caso, el procesamiento se reduce para alterar el valor de algunos atributos (no todos) de una secuencia de los mismos elementos con un conjunto de atributos, y tal vez insertar algunos elementos más.

¿Hay alguna manera "fácil" o tengo que definir mi propia hoja de estilo de transformación XSLT para especificar el resultado y alterar todo el archivo XML de entrada?

Actualización Debo agradecer todas sus respuestas. La respuesta parece ahora más obvia de lo que esperaba. Nunca presté atención al orden de los atributos, ya que nunca lo había necesitado antes.

La razón principal para requerir un orden de atributo es que el archivo XML resultante solo parece diferente. El objetivo es un archivo de configuración que contiene cientos de alarmas (cada alarma está definida por un conjunto de atributos). Este archivo generalmente tiene pequeñas modificaciones con el tiempo, pero es conveniente mantenerlo ordenado, ya que cuando tenemos que modificar algo, se edita a mano. De vez en cuando, algunos proyectos necesitan ligeras modificaciones de este archivo, como establecer uno de los atributos para un código específico del cliente.

Acabo de desarrollar una pequeña aplicación para fusionar el archivo original (común a todos los proyectos) con partes específicas de cada proyecto (modificar el valor de algunos atributos), para que el archivo específico del proyecto reciba las actualizaciones definiciones o algunas correcciones de errores de valores de atributo). Mi principal motivación para exigir atributos ordenados es poder verificar el resultado de la aplicación nuevamente el archivo original por medio de una herramienta de comparación de texto (como Winmerge). Si el formato (principalmente el orden de los atributos) sigue siendo el mismo, las diferencias se pueden detectar fácilmente.

Realmente pensé que esto era posible, ya que los programas de manejo de XML, como XML Spy, le permiten editar archivos XML y aplicar algunos pedidos (modo cuadrícula). Tal vez mi única opción es utilizar uno de estos programas en manualmente modificar el archivo de salida.

+0

* * ¿Por qué necesita para mantener un orden? La solicitud implica que está procesando el texto XML con herramientas que no se han creado para XML. Es ese el caso? – Tomalak

+0

La solución a su problema establecido es escribir un programa que preproceses los archivos para comparar antes de compararlos. Tal programa pondría los atributos en un orden canónico. –

+5

Commander @Tomalak, estoy procesando texto XML con herramientas que no se han hecho para XML: mis ojos. Xml también es un formato legible por humanos. –

Respuesta

20

Siento decirlo, pero la respuesta es más sutil que "No, no puedo" o "¿Por qué tiene que hacer esto en primer lugar?".

La respuesta corta es "DOM no te permitirá hacer eso, pero SAX lo hará".

Esto se debe a que a DOM no le importa el orden de los atributos, ya que no tiene ningún sentido en lo que respecta al estándar, y para cuando el XSL retiene la secuencia de entrada, la información ya está perdida. La mayoría de los motores XSL conservarán correctamente el orden de los atributos de la secuencia de entrada (por ejemplo, Xalan-C (excepto en un caso) o Xalan-J (siempre)). Especialmente si usa <xsl:copy*>.

Casos en los que no se guarda el orden de los atributos, lo que yo sepa. - Si el flujo de entrada es una de DOM - Xalan-C:. Si inserta las etiquetas resultado de árboles, literalmente (por ejemplo <elem att1={@att1} .../>

He aquí un ejemplo con SAX, para el registro (inhibición de DTD persistente también)

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

también me gustaría señalar, en la intención de muchos detractores que no son casos donde el orden atributo hace materia.

las pruebas de regresión es una ca obvia se. Quien haya sido llamado para optimizar XSL no tan bien escrito sabe que generalmente quiere asegurarse de que los árboles de resultados "nuevos" sean similares o idénticos a los "antiguos". Y cuando el árbol de resultados tiene alrededor de un millón de líneas, las herramientas XML diff resultan demasiado difíciles de manejar ... En estos casos, preservar el orden de los atributos es de gran ayuda.

Esperanza esto ayuda ;-)

+1

1, pero el ejemplo de las pruebas de regresión es una "cortina de humo". La solución es hacer que la herramienta de prueba de regresión primero convierta el archivo XML en un orden canónico antes de comparar. –

+4

@JohnSaunders Por otro lado, una de las cosas más bonitas de XML es que los humanos pueden leerlo, y al menos en los países LtoR, tendemos a buscar cosas importantes hacia la L, con cosas menos importantes para la R. Entonces, lo ideal sería bueno para preservar el orden cuando se crea el XML, como el creador puede haber considerado atributo ordenando a ser significativo para los seres humanos. Al menos, esto debería ser una opción en el escritor o el objeto del documento. – MushyMiddle

+0

Pero dado que XML no considera el orden de los atributos, eso no tendría mucho sentido. –

2

Realmente no necesita mantener ningún tipo de orden. Hasta donde yo sé, ningún esquema tiene en cuenta el orden de los atributos al validar un documento XML. Suena como que lo que está procesando XML en el otro extremo no usa un DOM apropiado para analizar los resultados.

Supongo que una opción sería crear manualmente el documento mediante la construcción de cadenas, pero recomiendo encarecidamente que no lo haga.

23

Mire la sección 3.1 de la recomendación de XML. Dice: "Tenga en cuenta que el orden de las especificaciones de atributos en una etiqueta de inicio o elemento vacío no es significativo".

Si una pieza de software requiere atributos en un elemento XML para aparecer en un orden específico, ese software no está procesando XML, está procesando texto que se ve superficialmente como XML. Necesita ser arreglado.

Si no se puede arreglar, y debe producir archivos que cumplan con sus requisitos, no puede usar de manera confiable las herramientas XML estándar para producir esos archivos. Por ejemplo, puede intentar (como sugiere) usar XSLT para producir atributos en un orden definido, p.:

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

sólo para encontrar que el procesador XSLT emite la siguiente:

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

porque el DOM que el procesador está utilizando órdenes atributos alfabéticamente por el nombre de la etiqueta. (Es un comportamiento común pero no universal entre los DOM XML.)

Pero quiero enfatizar algo. Si un software viola la recomendación de XML en un aspecto, probablemente lo viole en otros aspectos. Si se rompe al alimentar los atributos en el orden incorrecto, probablemente también se rompa si delimita los atributos con comillas simples, o si los valores de los atributos contienen entidades de caracteres, o cualquiera de una docena de otras cosas que la recomendación XML dice que un documento XML puede hacer lo que el autor de este software probablemente no pensó.

6

No es posible exagerar lo que Robert Rossney acaba de decir, pero lo intentaré. ;-)

El beneficio de los estándares internacionales es que, cuando todos los siguen, la vida es buena. Todo nuestro software se lleva bien.

XML tiene que ser uno de los estándares más importantes que tenemos. Es la base de cosas de "vieja web" como SOAP, y aún de cosas 'web 2.0' como RSS y Atom. Es debido a estándares claros que XML es capaz de interoperar entre diferentes plataformas.

Si nos damos por vencidos en XML, poco a poco, nos pondremos en una situación en la que un productor de XML no podrá suponer que un consumidor de XML podrá consumir su contenido. Esto tendría un efecto desastroso en la industria.

Deberíamos retroceder con mucha fuerza, en cualquier persona que escriba código que no procese XML de acuerdo con el estándar. Entiendo que, en estos tiempos económicos, hay una renuencia a ofender a los clientes y socios comerciales diciendo "no". Pero en este caso, creo que vale la pena. Estaríamos en una situación financiera mucho peor si tuviéramos que crear manualmente XML para cada socio comercial.

Por lo tanto, no "active" las empresas que no entienden XML. Envíeles el estándar, con las líneas apropiadas resaltadas. Deben dejar de pensar que XML es solo texto con corchetes angulares. Simplemente no se comporta como texto con corchetes angulares.

No es que haya una excusa para esto. Incluso los dispositivos integrados más pequeños pueden tener implementaciones de analizador XML completas. Todavía no escuché una buena razón para no poder analizar el XML estándar, incluso si uno no puede pagar una implementación DOM completa.

1

Robert Rossney lo dijo bien: si confía en el pedido de atributos, realmente no está procesando XML, sino algo que se parece a XML.

No puedo pensar en al menos dos razones por las que podría preocuparse por el orden de los atributos. Puede haber otros, pero al menos para estos dos me puede sugerir alternativas:

  1. Usted está utilizando varias instancias de atributos con el mismo nombre:

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

    Esto es sólo XML sin formato no válido; un procesador DOM probablemente dejará caer todos menos uno de estos valores, si procesa el documento en absoluto.En lugar de esto, se desea utilizar elementos secundarios:

    <foo> 
        <myChild="a"/> 
        <myChild="b"/> 
        <myChild="c"/> 
    </foo> 
    
  2. Estás suponiendo que algún tipo de distinción se aplica al atributo (s) que vienen por primera vez. Haga esto explícito, ya sea a través de otros atributos o mediante elementos secundarios. Por ejemplo:

    <foo attr1="a" attr2="b" attr3="c" theMostImportantAttribute="attr1" /> 
    
+2

En mi caso, estoy escribiendo un script de migración que manipula alguna configuración XML, que se almacena en el VCS. La diferencia de VCS muestra cambios sin sentido (modificación del orden de los atributos) así como cambios significativos (lo que el programa ha modificado). Sería bueno mostrar solo cambios significativos. También (aunque no es un problema que tengo) se producirán conflictos de fusión falsos si varias personas hacen este tipo de cosas y sus serializadores XML escribieron atributos no modificados en varios pedidos. –

+0

Algunas veces se está escribiendo un script para software de terceros/archivos de configuración. XML es forzado sobre ti. – kierans

+1

@AdrianSmith: su escenario se puede manejar procesando ambos lados de la comparación con anticipación con un script o hoja de estilo que emite el XML en un orden canónico. Lo he hecho para comparar los archivos .dtsx de SQL Server Integration Services, que tienen problemas mucho peores que el orden de los atributos. Entre otras cosas, estos archivos cambian simplemente abriéndolos en la herramienta de diseño. –

9

resultados Canonicalisation XML en un orden de atributos consistentes, principalmente para permiten verificar una firma sobre algunas o todas del XML, aunque hay otros usos potenciales. Esto puede adaptarse a tus propósitos.

+0

Aunque el problema ya no se aplica a mi situación actual, agradezco su respuesta. En un futuro cercano podría ser útil –

+0

Esta es la respuesta para las personas que buscan que XML sea comparable en pruebas y diferencias automáticas. Es por eso que se desarrolló la canonicalización. Tiene muchos [cambios] potenciales (https://www.ibm.com/developerworks/library/x-c14n/index.html) al documento, pero no me afectaron demasiado y los mantuve. Hice mi c14n con python 'lxml', fragmento de trabajo completo en https://stackoverflow.com/questions/22959577/python-exclusive-xml-canonicalization-xml-exc-c14n/22960033#22960033. – Noumenon

0

creo que puedo encontrar algunas justificaciones válidas por preocuparse por orden de atributo:

  • Usted puede estar esperando los seres humanos a tener que leer de forma manual, diagnosticar o editar los datos XML de un momento u otro; la legibilidad sería importante en esa instancia, y un ordenamiento consistente y lógico de los atributos ayuda con eso;
  • Puede que tenga que comunicarse con alguna herramienta o servicio que (erróneamente) se preocupa por el pedido; pedirle al proveedor que corrija su código puede no ser una opción: ¡pregúntalo a una agencia gubernamental mientras el plazo de tu usuario para entregar electrónicamente un montón de documentos fiscales se acerca más y más!

Parece que Alain Pannetier's solution es el camino a seguir.

También, es posible que desee echar un vistazo a DecentXML; le da un control total de cómo se formatea el XML, a pesar de que no es compatible con DOM. Especialmente útil si desea modificar un poco de XML editado a mano sin perder el formato.

+0

Tus humanos deberían aprender que el orden no importa. Y, sí, si su gobierno es del tipo que les quita los ingenieros de software disidentes y les dispara, entonces, no digan que no. Pero trate de encontrar una manera de decirnos qué gobierno es, para que sepamos para el futuro. –

+2

Lo siento, @John Saunders. La gente no necesita ser "enseñada" por el software, el software debe satisfacer las necesidades de la gente. Si tiene usuarios que pueden resultarle útiles para revisar atributos en un orden específico (tal vez para no hacer un trabajo de 15 minutos en 2 horas ...), debe hacerlo o es un ingeniero incompetente. La gente es lo primero. – Renascienza

+0

@ren No dije que debían ser enseñados por software. Dije que necesitan que les enseñen _sobre_ el software. XML funciona como funciona, no como las personas desinformadas se imaginan. Una implementación XML compatible puede presentar los atributos en cualquier orden y seguir siendo correcta. En este caso, el OP confunde la interfaz de usuario de una herramienta con el comportamiento del estándar. Necesitaba una herramienta de comparación que entendiera XML. –

1

Tuve el mismo problema exacto. Quería modificar los atributos XML, pero quería mantener el orden debido a la diferencia. Usé StAX para lograr esto. Debe usar XMLStreamReader y XMLStreamWriter (la solución basada en Cursor). Cuando obtiene un tipo de evento START_ELEMENT, el cursor mantiene el índice de los atributos. Por lo tanto, puede hacer modificaciones apropiadas y escribirlas en el archivo de salida "en orden".

Mira esto article/discussion. Puede ver cómo leer los atributos de los elementos de inicio en orden.

+0

Puede conservar el orden de los atributos con vtd-xml y solo con vtd-xml –

-1

Tengo un problema bastante similar. Necesito tener siempre el mismo atributo para el primero. Ejemplo:

<h50row a="1" xidx="1" c="1"></h50row> 
<h50row a="2" b="2" xidx="2"></h50row> 

debe convertirse en

<h50row xidx="1" a="1" c="1"></h50row> 
<h50row xidx="2" a="2" b="2"></h50row> 

he encontrado una solución con una expresión regular:

test = "<h50row a=\"1\" xidx=\"1\" c=\"1\"></h50row>"; 
test = test.replaceAll("(<h5.*row)(.*)(.xidx=\"\\w*\")([^>]*)(>)", "$1$3$2$4$5"); 

Esperamos que este útil

0

Todavía se puede hacer esto mediante el DOM estándar y API de transformación mediante el uso de una solución rápida y sucia como la que yo soy scribing:

Sabemos que la solución API de transformación ordena los atributos alfabéticamente. Puede ponerle un prefijo a los nombres de los atributos con algunas cadenas fáciles de pelar para que salgan en el orden que desee. Los prefijos simples como "a_", "b_", etc. deberían ser suficientes en la mayoría de las situaciones y pueden eliminarse fácilmente del xml de salida utilizando una regex de un liner.

Si está cargando un xml y resave y desea conservar el orden de los atributos, puede usar el mismo principio, primero modificando los nombres de los atributos en el texto xml de entrada y luego analizándolo en un objeto Document. De nuevo, realice esta modificación en base a un procesamiento textual del xml. Esto puede ser complicado, pero se puede hacer mediante la detección de elementos y sus cadenas de atributos, de nuevo, utilizando expresiones regulares. Tenga en cuenta que esta es una solución sucia. Hay muchas dificultades al analizar XML por su cuenta, incluso para algo tan simple como esto, así que tenga cuidado si decide implementarlo.

0

tipo de trabajos ...

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

En mi prueba simple que 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 
+0

No está realmente relacionado con esta pregunta.El chico no necesita ordenar elementos, sino atributos dentro de los elementos. – Renascienza

Cuestiones relacionadas