XSD: ¿cómo permitir elementos en cualquier orden cualquier número de veces?

109

Estoy tratando de crear un XSD y tratando de escribir la definición con el siguiente requisito:

  • Permitir que el elemento secundario especificado aparezca cualquier número de veces (de 0 a ilimitado)
  • Permitir que los elementos secundarios estén en cualquier orden

Miré a mi alrededor y encontré varias soluciones como esta :

<xs:element name="foo">
  <xsl:complexType>
    <xs:choice minOccurs="0" maxOccurs="unbounded">
      <xs:element name="child1" type="xs:int"/>
      <xs:element name="child2" type="xs:string"/>
    </xs:choice>
  </xs:complexType>
</xs:element>

Pero por lo que entiendo, xs: choice todavía solo permite la selección de un solo elemento. Por lo tanto, configurar MaxOccurs como ilimitado como este solo debería significar que "cualquiera" de los elementos secundarios puede aparecer varias veces. ¿Es esto exacto?

Si la solución anterior es incorrecta, ¿cómo puedo lograr lo que dije anteriormente en mi requisito?

EDITAR : ¿Qué pasa si el requisito es el siguiente?

  • El elemento child1 child2 puede aparecer cualquier número de veces (de 0 a ilimitado)
  • Elementos para estar en cualquier orden
  • Los elementos child3 y child4 deberían aparecer exactamente una vez.

Por ejemplo, este xml es válido:

<foo>
<child1> value </child1>
<child1> value </child1>
<child3> value </child3>
<child2> value </child2>
<child4> value </child4>
<child1> value </child1>
</foo>

pero esto no es (niño desaparecido3)

<foo>
<child1> value </child1>
<child1> value </child1>
<child2> value </child2>
<child4> value </child4>
<child1> value </child1>
</foo>
jvtech
fuente

Respuestas:

61

En el esquema que tiene en su pregunta, child1o child2puede aparecer en cualquier orden, cualquier número de veces. Entonces esto suena como lo que estás buscando.

Editar: si quisiera que solo uno de ellos aparezca un número ilimitado de veces, el ilimitado tendría que ir a los elementos en su lugar:

Editar: tipo fijo en XML.

Editar: O en mayúscula en maxOccurs

<xs:element name="foo">
   <xs:complexType>
     <xs:choice maxOccurs="unbounded">
       <xs:element name="child1" type="xs:int" maxOccurs="unbounded"/>
       <xs:element name="child2" type="xs:string" maxOccurs="unbounded"/>
     </xs:choice>
   </xs:complexType>
</xs:element>
xcut
fuente
básicamente sí, estoy buscando elementos child1, child2 para que aparezcan en cualquier orden, cualquier número de veces ... la respuesta que proporcionó aquí solo funciona para un solo elemento, ¿verdad? ¿O esto también resuelve mi requisito?
jvtech
El esquema de su pregunta cumple con sus requisitos; el esquema alternativo en mi respuesta es para un solo elemento. ¡Espero que eso lo aclare! :)
xcut
@Pavel, @xcut, Gracias por la aclaración, consulte el requisito editado ... ¿alguna idea?
jvtech
2
jvtech: no puede satisfacer ese requisito editado con un esquema XML; la única forma de lograrlo sería si child3 y child4 solo pueden aparecer al final. En cuyo caso, necesita una secuencia que contenga una opción y luego los dos elementos.
xcut
1
@ Daij-Djan También descubrí que no funcionaba. Intente agregar maxOccurs = "unbounded" en el elemento de elección para que se permita más de un elemento secundario.
MikeD
107

La formulación alternativa de la pregunta agregada en una edición posterior parece aún sin respuesta: cómo especificar que entre los hijos de un elemento, debe haber uno con nombre child3, uno con nombre child4y cualquier número con nombre child1o child2, sin restricción en el orden en que aparecen los niños.

Este es un lenguaje regular fácilmente definible, y el modelo de contenido que necesita es isomorfo a una expresión regular que define el conjunto de cadenas en el que los dígitos '3' y '4' ocurren exactamente una vez, y los dígitos '1' y '2 'ocurre cualquier número de veces. Si no es obvio cómo escribir esto, puede ser útil pensar en qué tipo de máquina de estados finitos construiría para reconocer dicho lenguaje. Tendría al menos cuatro estados distintos:

  • un estado inicial en el que no se ha visto ni '3' ni '4'
  • un estado intermedio en el que se ha visto '3' pero no '4'
  • un estado intermedio en el que se ha visto '4' pero no '3'
  • un estado final en el que se han visto tanto '3' como '4'

No importa en qué estado se encuentre el autómata, se pueden leer '1' y '2'; no cambian el estado de la máquina. En el estado inicial, también se aceptarán '3' o '4'; en los estados intermedios, solo se acepta '4' o '3'; en el estado final, no se aceptan ni '3' ni '4'. La estructura de la expresión regular es más fácil de entender si primero definimos una expresión regular para el subconjunto de nuestro lenguaje en el que solo aparecen '3' y '4':

(34)|(43)

Para permitir que '1' o '2' ocurra cualquier número de veces en una ubicación determinada, podemos insertar (1|2)*(o [12]*si nuestro lenguaje de expresiones regulares acepta esa notación). Al insertar esta expresión en todas las ubicaciones disponibles, obtenemos

(1|2)*((3(1|2)*4)|(4(1|2)*3))(1|2)*

Traducir esto a un modelo de contenido es sencillo. La estructura básica es equivalente a la expresión regular (34)|(43):

<xsd:complexType name="paul0">
  <xsd:choice>
    <xsd:sequence>
      <xsd:element ref="child3"/>
      <xsd:element ref="child4"/>
    </xsd:sequence>
    <xsd:sequence>
      <xsd:element ref="child4"/>
      <xsd:element ref="child3"/>
    </xsd:sequence>
  </xsd:choice>
</xsd:complexType>

Insertar una opción cero o más de child1y child2es sencillo:

<xsd:complexType name="paul1">
  <xsd:sequence>
    <xsd:choice minOccurs="0" maxOccurs="unbounded">
      <xsd:element ref="child1"/>
      <xsd:element ref="child2"/>
    </xsd:choice>      
    <xsd:choice>
      <xsd:sequence>
        <xsd:element ref="child3"/>
        <xsd:choice minOccurs="0" maxOccurs="unbounded">
          <xsd:element ref="child1"/>
          <xsd:element ref="child2"/>
        </xsd:choice>      
        <xsd:element ref="child4"/>
      </xsd:sequence>
      <xsd:sequence>
        <xsd:element ref="child4"/>
        <xsd:choice minOccurs="0" maxOccurs="unbounded">
          <xsd:element ref="child1"/>
          <xsd:element ref="child2"/>
        </xsd:choice>      
        <xsd:element ref="child3"/>
      </xsd:sequence>
    </xsd:choice>
    <xsd:choice minOccurs="0" maxOccurs="unbounded">
      <xsd:element ref="child1"/>
      <xsd:element ref="child2"/>
    </xsd:choice>      
  </xsd:sequence>
</xsd:complexType>

Si queremos minimizar un poco el volumen, podemos definir un grupo con nombre para las opciones repetidas de child1y child2:

<xsd:group name="onetwo">
  <xsd:choice>
    <xsd:element ref="child1"/>
    <xsd:element ref="child2"/>
  </xsd:choice>   
</xsd:group>

<xsd:complexType name="paul2">
  <xsd:sequence>
    <xsd:group ref="onetwo" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:choice>
      <xsd:sequence>
        <xsd:element ref="child3"/>
        <xsd:group ref="onetwo" minOccurs="0" maxOccurs="unbounded"/>
        <xsd:element ref="child4"/>
      </xsd:sequence>
      <xsd:sequence>
        <xsd:element ref="child4"/>
        <xsd:group ref="onetwo" minOccurs="0" maxOccurs="unbounded"/>
        <xsd:element ref="child3"/>
      </xsd:sequence>
    </xsd:choice>  
    <xsd:group ref="onetwo" minOccurs="0" maxOccurs="unbounded"/>
  </xsd:sequence>
</xsd:complexType>

En XSD 1.1, se eliminaron algunas de las restricciones en los allgrupos, por lo que es posible definir este modelo de contenido de manera más concisa:

<xsd:complexType name="paul3">
  <xsd:all>
    <xsd:element ref="child1" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:element ref="child2" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:element ref="child3"/>
    <xsd:element ref="child4"/>      
  </xsd:all>
</xsd:complexType>

Pero como puede verse en los ejemplos dados anteriormente, estos cambios en los allgrupos no cambian de hecho el poder expresivo del lenguaje; sólo hacen más sucinta la definición de ciertos tipos de lenguajes.

CM Sperberg-McQueen
fuente
3
Me gusta el XSD 1.0 xs: todas alternativas.
TWiStErRob
8
+1. Esta es una excelente respuesta y merece muchos más votos a favor.
Christoffer Lette
1
Gran respuesta ! Me gustan mucho las explicaciones como esta. Revela toda la lógica y el razonamiento detrás del logro de la meta. Ahora no solo sé cómo resolver este problema, sino que aprendí un nuevo enfoque para resolver problemas similares. Explicar esto usando una automatización de estado finito es una muy buena idea.
egelev
3
Michael, dices que "estos cambios en todos los grupos no cambian de hecho el poder expresivo del idioma; solo hacen que la definición de ciertos tipos de idiomas sea más concisa". Pero si generaliza el problema a cualquier número de elementos secundarios, un subconjunto de los cuales puede aparecer una vez y otro subconjunto que puede aparecer cualquier número de veces, la solución XSD 1.0 cedería a una explosión combinatoria, ¿no es así? Mientras que la solución XSD 1.1 permanecería limpia.
ebruchez
1
ebruchez, sí - fuerza expresiva , como yo uso el término, no es lo mismo que la concisión , compacidad , concisión , o manejabilidad . El poder expresivo solo pregunta "¿Puede este formalismo definir este lenguaje?" No pregunta sobre el tamaño de la gramática, o si algún azúcar sintáctico la haría más pequeña. La explosión combinatoria que menciona significa que manejar grandes conjuntos de elementos sin los cambios XSD 1.1 en todos los grupos se vuelve muy desagradable muy rápido (y para n grandes puede agotar la memoria). No significa que se vuelvan imposibles en principio.
CM Sperberg-McQueen
49

Esto es lo que finalmente funcionó para mí:

<xsd:element name="bar">
  <xsd:complexType>
    <xsd:sequence>
      <!--  Permit any of these tags in any order in any number     -->
      <xsd:choice minOccurs="0" maxOccurs="unbounded">
        <xsd:element name="child1" type="xsd:string" />
        <xsd:element name="child2" type="xsd:string" />
        <xsd:element name="child3" type="xsd:string" />
      </xsd:choice>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>
Alan
fuente
5
De hecho, el truco es usar xsd: choice con los cuantificadores <xsd: choice minOccurs = "0" maxOccurs = "unbounded">
tivo
6
Creo que vale la pena señalar que el ejemplo anterior funciona incluso sin el elemento de secuencia que encierra el elemento de elección.
9

Pero por lo que entiendo, xs: choice todavía solo permite la selección de un solo elemento. Por lo tanto, configurar MaxOccurs como ilimitado como este solo debería significar que "cualquiera" de los elementos secundarios puede aparecer varias veces. ¿Es esto exacto?

No. La elección ocurre individualmente por cada "repetición" xs:choiceque ocurre debido a maxOccurs="unbounded". Por lo tanto, el código que ha publicado es correcto y, de hecho, hará lo que desee tal como está escrito.

Pavel Minaev
fuente
Su comentario con la respuesta proporcionada por @Alan lo explica todo muy bien.
bor
3

Debería encontrar que el siguiente esquema permite lo que ha propuesto.

  <xs:element name="foo">
    <xs:complexType>
      <xs:sequence minOccurs="0" maxOccurs="unbounded">
        <xs:choice>
          <xs:element maxOccurs="unbounded" name="child1" type="xs:unsignedByte" />
          <xs:element maxOccurs="unbounded" name="child2" type="xs:string" />
        </xs:choice>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

Esto le permitirá crear un archivo como:

<?xml version="1.0" encoding="utf-8" ?>
<foo>
  <child1>2</child1>
  <child1>3</child1>
  <child2>test</child2>
  <child2>another-test</child2>
</foo>

Lo que parece coincidir con tu pregunta.

Steven_W
fuente
minOccursy maxOccursestán restringidos a 1 para niños de xs:all.
Pavel Minaev
Pavel: Gracias ... Descubrí esto después de revisar mi publicación y luego la
edité
1

Si nada de lo anterior está funcionando, probablemente esté trabajando en una transacción EDI donde necesita validar su resultado contra un esquema HIPPA o cualquier otro xsd complejo para el caso. El requisito es que, digamos que hay 8 segmentos REF y cualquiera de ellos tiene que aparecer en cualquier orden y además no todos son obligatorios, es decir que puede tenerlos en el siguiente orden 1º REF, 3º REF, 2º REF, 9º REF. En la situación predeterminada, la recepción de EDI fallará, porque el tipo complejo predeterminado es

<xs:sequence>
  <xs:element.../>
</xs:sequence>

La situación es incluso compleja cuando llama a su elemento por referencia y luego ese elemento en su lugar original es bastante complejo en sí mismo. por ejemplo:

<xs:element>
<xs:complexType>
<xs:sequence>
<element name="REF1"  ref= "REF1_Mycustomelment" minOccurs="0" maxOccurs="1">
<element name="REF2"  ref= "REF2_Mycustomelment" minOccurs="0" maxOccurs="1">
<element name="REF3"  ref= "REF3_Mycustomelment" minOccurs="0" maxOccurs="1">
</xs:sequence>
</xs:complexType>
</xs:element>

Solución:

Aquí, simplemente reemplazar "secuencia" con "todos" o usar "elección" con combinaciones mínimas / máximas no funcionará.

Lo primero que debe hacer es reemplazar "xs:sequence" with "<xs:all>" ahora, debe realizar algunos cambios desde donde está haciendo referencia al elemento, vaya a:

<xs:annotation>
  <xs:appinfo>
    <b:recordinfo structure="delimited" field.........Biztalk/2003">

*** Ahora, en el segmento anterior, agregue un punto de activación al final como este trigger_field = "REF01 _... nombre completo .." trigger_value = "38" Haga lo mismo para otros segmentos REF donde el valor de activación será diferente, como decir "18 "," XX "," YY ", etc., de modo que la información de su registro ahora se ve así:b:recordinfo structure="delimited" field.........Biztalk/2003" trigger_field="REF01_...complete name.." trigger_value="38">


Esto hará que cada elemento sea único, por lo que todos los segmentos de REF (ejemplo anterior) tienen la misma estructura que REF01, REF02, REF03. Y durante la validación, la validación de la estructura está bien, pero no permite que los valores se repitan porque intenta buscar los valores restantes en el primer REF. Agregar disparadores hará que todos sean únicos y pasarán en cualquier orden y casos situacionales (como usar 5 de 9 y no todos 9/9).

Espero que te ayude, porque pasé casi 20 horas en esto.

Buena suerte

Prabhdeep Gill
fuente