2009-09-16 22 views
5

Digamos que tengo un documento XML (representado como texto, un W3C DOM, lo que sea), y también un Esquema XML. El documento XML tiene todos los elementos correctos definidos por el esquema, pero en el orden incorrecto.Usar un esquema para reordenar los elementos de un documento XML de conformidad con el esquema

¿Cómo utilizo el esquema para "reordenar" los elementos en el documento para cumplir con el orden definido por el esquema?

Sé que esto debería ser posible, probablemente utilizando XSOM, ya que el generador de código JAXB XJC anota sus clases generadas con el orden correcto de serialización de los elementos.

Sin embargo, no estoy familiarizado con la API de XSOM, y es bastante denso, así que espero que alguno de ustedes tenga algo de experiencia y pueda orientarme en la dirección correcta. Algo así como "¿Qué elementos secundarios están permitidos dentro de este elemento primario y en qué orden?"


Déjeme dar un ejemplo.

que tienen un documento XML como esto:

<A> 
    <Y/> 
    <X/> 
</A> 

que tienen un esquema XML que dice que el contenido de <A> debe ser un <X> seguido de un <Y>. Ahora, claramente, si intento validar el documento con el esquema, falla, ya que <X> y <Y> están en el orden incorrecto. Pero sé que mi documento está "equivocado" de antemano, así que no estoy usando el esquema para validar por el momento. Sin embargo, I do sepa que mi documento tiene todos los elementos correctos definidos por el esquema, simplemente en el orden incorrecto.

Lo que quiero hacer es examinar mediante programación el esquema (probablemente utilizando XSOM, que es un modelo de objeto para el esquema XML), y preguntarle cuál debería ser el contenido de <A>. La API expondrá la información que "necesita un <X> seguido de un <Y>".

Así que tomo mi documento XML (usando una DOM API) y lo reorganizo y en consecuencia, de modo que ahora el documento se validará contra el esquema.

Es importante entender qué XSOM está aquí; es una API de Java que representa la información contenida en un Esquema XML, no la información contenida en mi documento de instancia.

Lo que no quiero hacer es generar código desde el esquema, ya que el esquema no se conoce en tiempo de compilación. Además, XSLT no sirve, ya que el ordenamiento correcto de los elementos está determinado únicamente por el diccionario de datos contenido en el esquema.

Esperemos que ahora sea lo suficientemente explícito.

+1

¿Cuáles son las limitaciones de la entrada? Usted da un ejemplo bastante simple, pero obviamente puede haber casos mucho más complicados como la elección (secuencia (elección (...))). Además, ¿se sabe de antemano que el documento de entrada puede ponerse en conformidad con el esquema reordenando los elementos? Si no es una garantía, francamente, no veo por dónde empezar. –

+0

Sí, sé de antemano que los elementos correctos están todos allí, pero el orden se ha aleatorizado de forma efectiva mediante un paso de procesamiento anterior. Tiene razón en que la complejidad potencial de las definiciones del tipo de esquema puede ser desalentadora, por lo que espero que XSOM pueda descifrar eso para mí y representarlo en términos simples. – skaffman

+1

Por lo que puedo ver, XSOM realmente no simplifica nada, es más como un DOM fuertemente tipado para el Esquema XML. En general, le compra la conveniencia de tener un analizador listo para usar y una estructura similar a AST para trabajar, pero nada que lo ayude en lo que desea. Entonces, la solución sería genérica, independientemente de la forma en que procese el esquema XML. –

Respuesta

2

Su problema se traduce en esto: tiene un archivo XSM que no coincide con el esquema y desea transformarlo en algo que sea válido.

Con XSOM, puede leer la estructura en el XSD y quizás analizar el XML, pero aún necesitaría una asignación adicional del formulario no válido al formulario válido. El uso de una hoja de estilo sería mucho más fácil, porque recorrería el XML, utilizando nodos XPath para manejar los elementos en el orden correcto. Con un XML en el que desea manzanas antes que peras, la hoja de estilo primero copiará el nodo Apple (/ Fruit/Apple) antes de copiar el nodo pera.De esa forma, sin importar el orden en el archivo anterior, estarían en el orden correcto en el nuevo archivo.

Lo que podría hacer con XSOM es leer el XSD y generar la hoja de estilos que reordene los datos. Luego transforma el XML usando esa hoja de estilo. una vez que XSOM ha generado una hoja de estilo para el XSD, puede volver a utilizar la hoja de estilos hasta que se modifique el XSD o se necesite otro XSD.

Por supuesto, puede usar XSOM para copiar nodos inmediatamente en el orden correcto. Pero dado que esto significa que su código tiene que recorrer todos los nodos y los nodos secundarios, podría llevar algún tiempo procesar para finalizar. Una hoja de estilo haría lo mismo, pero el transformador podrá procesarlo todo más rápido. Puede trabajar directamente en los datos, mientras que el código de Java tendría que obtener/configurar cada nodo a través de las propiedades de XMLDocument.


Por lo tanto, usaría XSOM para generar una hoja de estilo para el XSD que simplemente copiaría el nodo XML por nodo para volver a usarlo una y otra vez. La hoja de estilo solo tendría que ser reescrita cuando el XSD cambie y funcionaría más rápido que cuando la API de Java necesita pasar por los nodos. A la hoja de estilos no le importa el orden, por lo que siempre terminaría en el orden correcto.
Para hacerlo más interesante, puede omitir XSOM e intentar trabajar con una hoja de estilos que lea el XSD para generar otra hoja de estilo. Esta hoja de estilos generada estaría copiando los nodos XML en el orden exacto como se define en la hoja de estilos. ¿Sería complejo? En realidad, la hoja de estilo necesitaría generar plantillas para cada elemento y asegurarse de que los elementos secundarios de este elemento se procesen en el orden correcto.

Cuando pienso sobre esto, me pregunto si esto ya se ha hecho antes. Sería muy genérico y podría manejar casi todos los XSD/XML.

Veamos ... Usando "// xsd: element/@ name" obtendría todos los nombres de elementos en el esquema. Cada único nombre tendría que ser traducido a una plantilla. Dentro de estas plantillas, necesitaría procesar los nodos secundarios del elemento específico, que es un poco más complejo de obtener. Los elementos pueden tener una referencia, que deberías seguir. De lo contrario, obtenga todos los nodos child xsd: element.

+0

Sí, ese es el camino ir. –

+0

OK, genial, ambos estamos en la misma página ahora :) Estoy de acuerdo en que una transformación XSL reorganizaría mi documento de manera más eficiente que manualmente hurgando en el DOM, pero el problema inicial de usar la API XSOM para averiguar lo que la orden * debería * ser permanece, independientemente del mecanismo que utilizo para realizar el reordenamiento. – skaffman

+0

De repente me pregunto si no es posible usar una hoja de estilo para transformar un XSD en una hoja de estilo de copia de XML. Sería una solución interesante multiplataforma. Si ya está familiarizado con XSD y XSLT, esto podría ser más fácil que tener que aprender más acerca de XSOM. –

3

Todavía no tengo una buena respuesta para esto, pero debo señalar que existe la posibilidad de ambigüedad. Considere este esquema:

<xs:element name="root"> 
    <xs:choice> 
    <xs:sequence> 
     <xs:element name="foo"/> 
     <xs:element name="bar"> 
     <xs:element name="dee"> 
     <xs:element name="dum"> 
     </xs:element> 
    </xs:sequence> 
    <xs:sequence> 
     <xs:element name="bar"> 
     <xs:element name="dum"> 
     <xs:element name="dee"> 
     </xs:element> 
     <xs:element name="foo"/> 
    </xs:sequence> 
    </xs:choice> 
</xs:element> 

y esto XML de entrada:

<root> 
    <foo/> 
    <bar> 
    <dum/> 
    <dee/> 
    </bar> 
</root> 

Esto se podría hacer para cumplir con el esquema ya sea mediante la reordenación de <foo> y <bar>, o reordenando <dee> y <dum>. No parece haber ninguna razón para preferir uno sobre otro.

+0

Bien manchado, ese es un punto justo. En mi caso, sin embargo, sé que tal ambigüedad no surgiría, ya que cada '' tendría el mismo tipo de esquema, con el mismo orden secundario. – skaffman

+0

Buen punto (+1), pero ¿cuán comunes serían tales construcciones? ¿Y por qué alguien usaría tal construcción? –

1

Básicamente, desea tomar el elemento raíz y desde allí examinar recursivamente los elementos secundarios del documento y los elementos secundarios definidos en el esquema y hacer coincidir la orden.

Te daré una solución de sintaxis C#, ya que es lo que código en el día y la noche, es muy similar a Java. Tenga en cuenta que tendré que hacer conjeturas acerca de XSOM ya que no sé si es API. También inventé los métodos XML Dom, dado que dar los C# correctamente no ayudaría :)

// asumir que la primera llamada es SortChildrenIntoNewDocument (sourceDom.DocumentElement, targetDom.DocumentElement, schema.RootElement)

public void SortChildrenIntoNewDocument(XmlElement source, XmlElement target, SchemaElement schemaElement) 
{ 
    // whatever method you use to ask the XSOM to tell you the correct contents 
    SchemaElement[] orderedChildren = schemaElement.GetChildren(); 
    for(int i = 0; i < orderedChildren.Length; i++) 
    { 
     XmlElement sourceChild = source.SelectChildByName(orderedChildren[ i ].Name); 
     XmlElement targetChild = target.AddChild(sourceChild) 
     // recursive-call 
     SortChildrenIntoNewDocument(sourceChild, targetChild, orderedChildren[ i ]); 
    } 
} 

que no recomendaría un método recursivo si va a ser un árbol de profundidad, en ese caso tendría que crear objetos de tipo alguna 'árbol Walker'. La ventaja de este enfoque es que podrá manejar cosas más complejas, como cuando el esquema dice que puede tener 0 o más elementos, puede seguir procesando nodos de origen hasta que no haya más coincidencias, luego mueva el esquema andador. a partir de ahí.

+0

No es tan simple como eso, porque no hay 'getChildren()' - como yo entiendo, podría haber cosas como 'xs: choice', o' maxOccurs> 1', por lo que puede que ni siquiera haya un solo elemento específico como Enésimo niño - sería "X o Y o ...", esencialmente largo arbitrario. –

+0

Pensé que podría ser así (dada la naturaleza de XSD), por lo que la segunda opción que mencioné es la única manera de ir realmente. Si nadie más viene con una solución, puedo publicar un ejemplo de cómo funcionaría. –

3

Me quedé con el mismo problema durante unas dos semanas. Finalmente obtuve el gran avance. Esto se puede lograr utilizando la función de clasificación/desemparejamiento JAXB.

En JAXB mariscal/unmarshal, la validación de XML es una función opcional. Al crear objetos Marshaller y UnMarshaller, no llamamos al método setSchema (schema). Omitir este paso evita la función de validación XML de mariscal/unmarshal.

Así que ahora,

  1. Si algún elemento obligatorio según XSD no está presente en XML, que es pasado por alto.
  2. Si alguna etiqueta no presente en XSD está presente en XML, no se produce ningún error y no está presente en el nuevo XML obtenido después de ordenar/desasignar.
  3. Si los elementos no están en secuencia, se reordenan. Esto lo hacen los POJO generados por JAXB que pasamos al crear JAXBContext.
  4. Si un elemento está fuera de lugar dentro de alguna otra etiqueta, entonces, se omite en el nuevo XML. No se genera ningún error mientras se calcula/clasifica.

public class JAXBSequenceUtil { 
    public static void main(String[] args) throws JAXBException, IOException { 

    String xml = FileUtils.readFileToString(new File(
      "./conf/out/Response_103_1015700001&^&IOF.xml")); 

    System.out.println("Before marshalling : \n" + xml); 
    String sequencedXml = correctSequence(xml, 
      "org.acord.standards.life._2"); 
    System.out.println("After marshalling : \n" + sequencedXml); 
    } 

    /** 
    * @param xml 
    *   - XML string to be corrected for sequence. 
    * @param jaxbPackage 
    *   - package containing JAXB generated classes using XSD. 
    * @return String - xml with corrected sequence 
    * @throws JAXBException 
    */ 
    public static String correctSequence(String xml, String jaxbPackage) 
     throws JAXBException { 
    JAXBContext jaxbContext = JAXBContext.newInstance(jaxbPackage); 
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 
    Object txLifeType = unmarshaller.unmarshal(new InputSource(
      new StringReader(xml))); 
    System.out.println(txLifeType); 

    StringWriter stringWriter = new StringWriter(); 
    Marshaller marshaller = jaxbContext.createMarshaller(); 
    marshaller.marshal(txLifeType, stringWriter); 

    return stringWriter.toString(); 
    } 
} 
Cuestiones relacionadas