2010-08-05 10 views
0

Je souhaite utiliser la syntaxe compacte RELAX NG pour valider un élément XML dont les enfants sont un, deux, trois ou n d'un ensemble de n éléments spécifiques. Par exemple, si l'élément est « mise en page » et il existe un ensemble de trois éléments spécifiques: « top », « centre » et « bas », les définitions d'éléments XML suivants seraient valides:Définition ordonnée/non ordonnée des enfants d'un élément XML utilisant la syntaxe compacte RELAX NG

<Layout> 
    <top/> 
    <center/> 
    <bottom/> 
</Layout> 

<Layout> 
    <top/> 
</Layout> 

<Layout> 
    <center/> 
</Layout> 

<Layout> 
    <bottom/> 
</Layout> 

<Layout> 
    <top/> 
    <center/> 
</Layout> 

Je veux pour savoir comment écrire deux modèles: 1) Permettre aux enfants d'être dans n'importe quel ordre. 2) Restreindre les enfants dans un ordre spécifique (par exemple: haut, centre, bas).

La solution que j'ai jusqu'à présent pour l'exemple XML et le modèle commandé est:

element Layout { 
    (
     element top { text }, 
     element center { text }? 
    ) |(
     element top { text }?, 
     element center { text }?, 
     element bottom { text } 
    ) |(
     element center { text } 
    ) 
} 

Je n'ai pas une bonne solution pour plus de 3 éléments et/ou pour un motif non ordonné.

Répondre

1

Celles-ci ont été discutées récemment sur le RelaxNG list (j'ai posé la question comme cela arrive). La restriction que vous essayez de codifier dans le premier cas est quelque chose du genre «permettre zéro ou un chacun d'un ensemble d'éléments», je pense. Il n'y a pas de bonne façon de le faire mais vous pouvez faire quelque chose. Si vous avez beaucoup d'éléments enfants possibles, c'est pour le moins fastidieux (j'ai huit dans mon schéma et c'est suffisamment de douleur honnêtement). Quelque chose comme ceci devrait fonctionner pour le cas 1.

J'ai utilisé des modèles nommés pour rendre cela plus lisible.

start = e.layout 
e.top = element top { text } 
e.center = element center { text } 
e.bottom = element bottom { text } 
e.layout = element Layout { 
    (e.top & e.center? & e.bottom?) |  
    (e.top? & e.center & e.bottom?) | 
    (e.top? & e.center? & e.bottom) 
} 

Cela nécessite l'un des éléments plus zéro ou l'un de chacun des deux autres.

Pour appliquer une séquence, vous pouvez faire une chose semblable, mais utiliser le « » opérateur à la place que vous avez fait:

start = e.layout 
e.top = element top { text } 
e.center = element center { text } 
e.bottom = element bottom { text } 
e.layout = element Layout { 
    (e.top, e.center?) | 
    (e.top, e.center, e.bottom?) |   
    (e.top, e.center, e.bottom) |  
    (e.top, e.bottom?) 
} 

Si vous allez obtenir plus complexe que cela, je envisager sérieusement d'écrire beaucoup plus simple qui correspond simplement aux éléments appropriés et ensuite utiliser une règle de Schematron pour appliquer les comptes. Donc, pour votre deuxième exigence:

<?xml version="1.0" encoding="UTF-8"?> 
<grammar xmlns="http://relaxng.org/ns/structure/1.0" 
     xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
    <start> 
    <ref name="e.Layout"/> 
    </start> 
    <define name="e.top"> 
    <element name="top"> 
     <text/> 
    </element> 
    </define> 
    <define name="e.center"> 
    <element name="center"> 
     <text/> 
    </element> 
    </define> 
    <define name="e.bottom"> 
    <element name="bottom"> 
     <text/> 
    </element> 
    </define> 
    <define name="e.Layout"> 
    <sch:pattern name="check no more than one of each"> 
     <sch:rule context="Layout/*"> 
     <sch:assert test="count(../*[local-name(.) eq local-name(current())]) = 1">You may only have one <name/> element as a child of Layout.</sch:assert> 
     </sch:rule> 
    </sch:pattern> 
    <element name="Layout"> 
     <oneOrMore> 
     <group> 
      <ref name="e.top"/> 
      <ref name="e.center"/> 
      <ref name="e.bottom"/> 
     </group> 
     </oneOrMore> 
    </element> 
    </define> 
</grammar> 

ou, dans la syntaxe compacte (annotations ne sont pas assez en rnc):

namespace sch = "http://purl.oclc.org/dsdl/schematron" 

start = e.Layout 
e.top = element top { text } 
e.center = element center { text } 
e.bottom = element bottom { text } 
[ 
    sch:pattern [ 
    name = "check no more than one of each" 
    "\x{a}" ~ 
    "  " 
    sch:rule [ 
     context = "Layout/*" 
     "\x{a}" ~ 
     "  " 
     sch:assert [ 
     test = "count(../*[local-name(.) eq local-name(current())]) = 1" 
     "You may only have one " rng:name [ ] " element as a child of Layout" 
     ] 
     "\x{a}" ~ 
     "  " 
    ] 
    "\x{a}" ~ 
    " " 
    ] 
] 
e.Layout = element Layout { (e.top, e.center, e.bottom)+ } 
0

Vous pouvez utiliser « top {texte} élément ?, centre élément {texte } ?, élément bottom {text}? " pour obtenir zéro ou un de chaque dans l'ordre indiqué

Ou utilisez "élément top {text}? & élément centre {texte}? & élément bottom {text}?" pour obtenir zéro ou un de chaque dans n'importe quel ordre.

Ces deux modèles sont extensibles à arbitrairement beaucoup d'éléments.

+0

Cela ne validerait également aucun élément. Dans l'exemple, 'Mise en page' devrait être non vide. – Javier

+0

Bon point. Pour exclure cela, vous avez besoin d'un choix contenant N sous-modèles pour les N types d'éléments enfants, quelque chose comme "(haut & centre? & Bas?) | (Haut & centre & bas?) | (Haut & centre & bas?) ", ou la même chose avec des virgules au lieu d'ampersands si vous voulez commander.Au moins c'est seulement une explosion linéaire, pas une combinatoire. –

1

Je ne sais pas si cela va vous aider, mais nous l'avons utilisé. Notre problème: Deux éléments, n'importe quel ordre, les deux peuvent apparaître 1 à n quantité de fois Solution: <interleave> <oneOrMore> <ref name="a.class"/> </oneOrMore> <oneOrMore> <ref name="b.class"/> </oneOrMore> </interleave> Pour ordonné il suffit de supprimer <interleave>. Pour 0 à n occurrences, utilisez <zeroOrMore>.