XSD - como permitir elementos em qualquer ordem qualquer número de vezes?

109

Estou tentando criar um XSD e tentando escrever a definição com o seguinte requisito:

  • Permitir que o elemento filho especificado apareça qualquer número de vezes (0 a ilimitado)
  • Permitir que os elementos filhos estejam em qualquer ordem

Olhei em volta e encontrei várias soluções 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>

Mas, pelo que entendi, xs: choice ainda permite a seleção de um único elemento. Portanto, definir MaxOccurs como ilimitado dessa forma deve significar apenas que "qualquer um" dos elementos filho pode aparecer várias vezes. Isso é correto?

Se a solução acima estiver incorreta, como posso alcançar o que afirmei acima em meu requisito?

EDITAR : E se o requisito for o seguinte?

  • O elemento filho1 filho2 pode aparecer qualquer número de vezes (0 a ilimitado)
  • Elementos em qualquer ordem
  • Os elementos child3 e child4 devem aparecer exatamente uma vez.

Por exemplo, este xml é válido:

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

mas isso não é (criança ausente3)

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

Respostas:

61

No esquema que você tem em sua pergunta, child1ou child2pode aparecer em qualquer ordem, qualquer número de vezes. Então, isso soa como o que você está procurando.

Editar: se você quiser que apenas um deles apareça um número ilimitado de vezes, o ilimitado terá que ir para os elementos em vez disso:

Editar: tipo fixo em XML.

Editar: O em maiúsculo em 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
fonte
basicamente sim, estou procurando os elementos child1, child2 para aparecerem em qualquer ordem, qualquer número de vezes .. a resposta que você forneceu aqui só funciona para um elemento, certo? ou isso resolve minha necessidade também?
jvtech
O esquema em sua pergunta atende aos seus requisitos; o esquema alternativo em minha resposta é para um único elemento. Espero que isso esclareça tudo! :)
xcut
@Pavel, @xcut, Obrigado pelo esclarecimento, consulte o requisito editado .. alguma opinião?
jvtech
2
jvtech: você não pode satisfazer esse requisito editado com o esquema XML; a única maneira de conseguir isso seria se child3 e child4 pudessem aparecer apenas no final. Nesse caso, você precisa de uma sequência contendo uma escolha e, em seguida, os dois elementos.
xcut
1
@ Daij-Djan Também descobri que não funcionava. Tente adicionar maxOccurs = "unbounded" no elemento choice para que mais de um elemento filho seja permitido.
MikeD
107

A formulação alternativa da questão adicionada em uma edição posterior parece ainda sem resposta: como especificar que entre os filhos de um elemento, deve haver um nomeado child3, um nomeado child4e qualquer número nomeado child1ou child2, sem restrição na ordem em que as crianças aparecem.

Esta é uma linguagem regular definida de forma direta, e o modelo de conteúdo de que você precisa é isomórfico a uma expressão regular que define o conjunto de strings em que os dígitos '3' e '4' ocorrem exatamente uma vez, e os dígitos '1' e '2 'ocorrer qualquer número de vezes. Se não for óbvio como escrever isso, pode ser útil pensar sobre que tipo de máquina de estado finito você construiria para reconhecer tal linguagem. Teria pelo menos quatro estados distintos:

  • um estado inicial em que nem '3' nem '4' foi visto
  • um estado intermediário em que '3' foi visto, mas não '4'
  • um estado intermediário em que '4' foi visto, mas não '3'
  • um estado final em que '3' e '4' foram vistos

Não importa em que estado o autômato esteja, '1' e '2' podem ser lidos; eles não mudam o estado da máquina. No estado inicial, '3' ou '4' também serão aceitos; nos estados intermediários, apenas '4' ou '3' é aceito; no estado final, nem '3' nem '4' é aceito. A estrutura da expressão regular é mais fácil de entender se primeiro definirmos um regex para o subconjunto de nossa linguagem em que apenas '3' e '4' ocorrem:

(34)|(43)

Para permitir que '1' ou '2' ocorra qualquer número de vezes em um determinado local, podemos inserir (1|2)*(ou [12]*se nossa linguagem regex aceitar essa notação). Inserindo esta expressão em todos os locais disponíveis, obtemos

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

Traduzir isso em um modelo de conteúdo é simples. A estrutura básica é equivalente ao regex (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>

Inserir uma opção zero ou mais de child1e child2é simples:

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

Se quisermos minimizar o volume um pouco, podemos definir um grupo nomeado para as opções de repetição de child1e 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>

No XSD 1.1, algumas das restrições nos allgrupos foram eliminadas, então é possível definir este modelo de conteúdo de forma mais 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>

Mas, como pode ser visto nos exemplos dados anteriormente, essas mudanças em allgrupos não alteram de fato o poder expressivo da linguagem; eles apenas tornam a definição de certos tipos de linguagens mais sucinta.

CM Sperberg-McQueen
fonte
3
Eu gosto do XSD 1.0 xs: all alternative.
TWiStErRob
8
+1. Esta é uma excelente resposta e merece muito mais votos positivos.
Christoffer Lette
1
Ótima resposta ! Eu realmente gosto de explicações como essa. Ele revela toda a lógica e o raciocínio por trás da conquista do objetivo. Agora eu não só sei como resolver esse problema, mas aprendi uma nova abordagem para resolver problemas semelhantes. Explicar isso usando uma automação de estado finito é uma ideia muito boa.
egelev
3
Michael, você diz "essas mudanças para todos os grupos não mudam de fato o poder expressivo da linguagem; elas apenas tornam a definição de certos tipos de linguagens mais sucinta". Mas se você generalizar o problema para qualquer número de elementos filhos, um subconjunto dos quais pode aparecer uma vez e outro subconjunto que pode aparecer qualquer número de vezes, a solução XSD 1.0 resultaria em uma explosão combinatória, não é? Enquanto a solução XSD 1.1 permaneceria limpa.
ebruchez
1
ebruchez, sim - poder expressivo , como uso o termo, não é o mesmo que sucinto , compacto , conciso ou gerenciabilidade . O poder expressivo apenas pergunta: "Esse formalismo pode definir essa linguagem?" Ele não pergunta sobre o tamanho da gramática ou se algum açúcar sintático a tornaria menor. A explosão combinatória que você mencionou significa que lidar com grandes conjuntos de elementos sem as alterações do XSD 1.1 em todos os grupos se torna muito desagradável muito rápido (e para n grandes pode esgotar a memória). Isso não significa que se tornem impossíveis em princípio.
CM Sperberg-McQueen
49

Isso é o que finalmente funcionou para mim:

<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
fonte
5
De fato, o truque é usar xsd: choice com os quantificadores <xsd: choice minOccurs = "0" maxOccurs = "unbounded">
tivo
6
Acho que vale a pena apontar que o exemplo acima funciona mesmo sem o elemento de sequência envolvendo o elemento de escolha.
9

Mas, pelo que entendi, xs: choice ainda permite a seleção de um único elemento. Portanto, definir MaxOccurs como ilimitado dessa forma deve significar apenas que "qualquer um" dos elementos filho pode aparecer várias vezes. Isso é correto?

Não. A escolha acontece individualmente para cada "repetição" do xs:choiceque ocorre devido a maxOccurs="unbounded". Portanto, o código que você postou está correto e realmente fará o que você quiser conforme escrito.

Pavel Minaev
fonte
Seu comentário com a resposta fornecida por @Alan explica tudo muito bem.
bor
3

Você deve descobrir que o seguinte esquema permite o que você propôs.

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

Isso permitirá que você crie um arquivo como:

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

O que parece corresponder à sua pergunta.

Steven_W
fonte
minOccurse maxOccurssão restritos a 1 para filhos de xs:all.
Pavel Minaev
Pavel: Obrigado ... Descobri isso depois de verificar novamente minha postagem e, em seguida, editei para remover xs: all
Steven_W
1

Se nenhum dos itens acima estiver funcionando, você provavelmente está trabalhando na tração EDI, onde precisa validar seu resultado em um esquema HIPPA ou qualquer outro xsd complexo para esse assunto. O requisito é que, digamos que haja 8 segmentos REF e qualquer um deles tenha que aparecer em qualquer ordem e também nem todos são necessários, significa dizer que você pode tê-los na seguinte ordem 1 REF, 3 REF, 2 REF, 9 REF. Na situação padrão, o recebimento de EDI falhará, porque o tipo complexo padrão é

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

A situação é até mesmo complexa quando você está chamando seu elemento por referência e então aquele elemento em seu local original é bastante complexo em si mesmo. por exemplo:

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

Solução:

Aqui, simplesmente substituir "sequência" por "todos" ou usar "escolha" com combinações mín / máx não funcionará!

Substitua a primeira coisa "xs:sequence" with "<xs:all>" agora, você precisa fazer algumas alterações de onde está se referindo ao elemento. Vá para:

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

*** Agora, no segmento acima, adicione o ponto de acionamento no final como este trigger_field = "REF01 _... nome completo .." trigger_value = "38" Faça o mesmo para outros segmentos REF onde o valor do acionador será diferente, como digamos "18 "," XX "," YY "etc .. para que as informações do seu registro agora se pareçam com:b:recordinfo structure="delimited" field.........Biztalk/2003" trigger_field="REF01_...complete name.." trigger_value="38">


Isso fará com que cada elemento seja único, sendo que todos os segmentos REF (exemplo acima) têm a mesma estrutura como REF01, REF02, REF03. E durante a validação a validação da estrutura está ok, mas não deixa os valores se repetirem porque tenta procurar os valores restantes no próprio REF. Adicionar gatilhos os tornará todos únicos e eles passarão em qualquer ordem e casos situacionais (como usar 5 em 9 e não todos 9/9).

Espero que ajude você, pois gastei quase 20 horas nisso.

Boa sorte

Prabhdeep Gill
fonte