Jan: xml-schema: key und keyref

Hallo allerseits,

ich hab ein bisschen Schwierigkeiten mit XPath und der Verwendung von key und keyref bei der XML Schema Definition. Vielleicht kann mir jemand helfen, der sich damit auskennt. Mein Problem sieht grob wie folgt aus:

  
<root>  
  <a name="s">  
    ...  
    <b name="v">  
    <b name="w">  
    <b name="x">  
    ...  
  </a>  
  <a name="t">  
    ...  
    <b name="x">  
    <b name="y">  
    <b name="z">  
    ...  
  </a>  
  ...  
  ...  
  <c context="t">  
    <d id="z">  
  </c>  
</root>  

Ich habe eine Reihe von Elementen 'b' deren 'name'-Attribute innerhalb des übergeordneten Elements 'a' unterschiedlich (unique) sein müssen. Zwei 'b' Elemente dürfen jedoch denselben Namen haben, wenn der Wert der 'name'-Attribute ihrer beiden Parent-Elemente unterschiedlich sind.

Ich muss also irgendwie eine key definieren, der zwei xsd:field Felder mit dem Namen von 'a' und dem Namen von 'b' hat und daraus Tupel der Form (s,v) (s,w) (s,x) (t,x) (t,y) (t,z) bildet und auf ihre Eindeutigkeit überprüft. Später tauchen, wieder auf unterschiedlichen Ebenen, Identifier (id="z") in einem Kontext (context="t") auf, für die ich mittels keyref überprüfen muss, ob (t,z) zuvor definiert war.

Ich hoffe es ist halbwegs verständlich geworden, was ich vorhabe. Meine Frage ist, in welchem Element definiert man am Besten key und keyref und wie müssen die XPath Pfade aussehen, damit die Attribute aus unterschiedlichen Ebenen derart vertupelt werden. Ich hab schon ewig rumprobiert und es nicht hinbekommen.

Danke für jede Antwort

Jan

  1. OK, ich weiss jetzt ungefähr wo mein Problem liegt. Den key zu erstellen ist noch einfach. Aber bei der richtigen keyref hakt es dann.

      
    <xsd:element name="root" type="rootType">  
      
      <xsd:key name="myKey">  
        <xsd:selector xpath="a"/>  
        <xsd:field xpath="b/@name"/>  
        <xsd:field xpath="@name"/>  
      </xsd:key>  
      
      <xsd:keyref name="myRef1" refer="myKey">  
          <xsd:selector xpath="."/>  
          <xsd:field xpath="c/d/@id"/>  
          <xsd:field xpath="c/@context"/>  
        </xsd:keyref>  
      
      <xsd:keyref name="myRef2" refer="myKey">  
          <xsd:selector xpath="c"/>  
          <xsd:field xpath="d/@id"/>  
          <xsd:field xpath="@context"/>  
      </xsd:keyref>  
      
      <xsd:keyref name="myRef3" refer="myKey">  
          <xsd:selector xpath="c/d"/>  
          <xsd:field xpath="@id"/>  
          <xsd:field xpath="../@context"/>  
      </xsd:keyref>  
      
    </xsd:element>  
    
    

    myRef1 überprüft nur, ob das allererste d-Element im allerersten c-Element der Schlüsselbedingung genügt. Falls nicht gibt es eine schöne Fehlermeldung, falls doch werden alle anderen d-Elemente ignoriert.

    myRef2 überprüft für jedes c-Element, ob das erste d-Element die Schlüsselbedingung erfüllt. Falls nicht gibt es die Fehlermeldung, falls doch werden alle weiteren d-Elemente in diesem c-Element ignoriert.

    Ich muss also offensichtlich mit dem Selektor direkt auf das d-Element zeigen, damit alle Elemente überprüft werden. Wenn ich es so versuche wie in myRef3, dann bekomme ich aber die Fehlermeldung:

    c-general-xpath: The expression '../@context' is not valid with respect to the XPath subset supported by XML Schema.

    Aber irgendwie muss ich mit XPath mein xsd:field auf ein Attribut des unmittelbaren Eltern-Element des im Selektor ausgewählten Elements verweisen lassen. Kann man nicht irgendwo nachlesen, welches Subset von XPath in XML Schema erlaubt ist?

    Grüße Jan

    1. Da stehts ja:
      http://www.w3.org/TR/2001/PR-xmlschema-1-20010330/#coss-identity-constraint

      Path in Field XPath expressions
      [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )

      Man kann also offensichtlich mit der xsd:field Expression nur auf Elemente verweisen, die noch tiefer im Baum liegen. Also muss ich den xsd:selector eine Ebene höher ansetzen und dann überprüft der Validierer immer nur, ob das erste d-Element in jedem c-Element dem Schlüssel entspricht und ignoriert alle weiteren, weil die Bedingung ja schon erfüllt ist. Tja watt nun?

      1. Hallo,

        Tja watt nun?

        Du bist meiner Antwort vorgekommen, hätte dir das gleiche gesagt, wie du es nun herausgefunden hast (mehr oder weniger).

        Dein key:

          
        
        >   <xsd:key name="myKey">  
        >     <xsd:selector xpath="a"/>  
        >     <xsd:field xpath="b/@name"/>  
        >     <xsd:field xpath="@name"/>  
        >   </xsd:key>  
        
        

        funktioniert nicht, denn bei einer Struktur wie:

          
         <a name="s">  
          <b name="v">..</b>  
          <b name="w">..</b>  
          <b name="x">..</b>  
         </a>  
         <a name="t">  
          <b name="x">..</b>  
          <b name="y">..</b>  
          <b name="z">..</b>  
         </a>  
         <a name="u">  
          <b name="x">..</b>  
          <b name="y">..</b>  
          <b name="z">..</b>  
         </a>  
        
        

        produziert b/@name mehrere Treffer.
        Das kann man noch verhindern:

          
         <xs:element name="root">  
          <xs:key name="KeyAB1">  
           <xs:selector xpath="a" />  
           <xs:field xpath="@name" />  
          </xs:key>  
         </xs:element>  
          
         <xs:element name="a">  
          <xs:key name="KeyAB2">  
           <xs:selector xpath="b"></xs:selector>  
           <xs:field xpath="@name"></xs:field>  
          </xs:key>  
         </xs:element>  
        
        

        jetzt muss man <c> noch referenzieren:

          
         <xs:element name="root">  
          <xs:key name="KeyAB1">  
           <xs:selector xpath="a" />  
           <xs:field xpath="@name" />  
          </xs:key>  
          <xs:keyref refer="KeyAB1" name="RefKeyC">  
           <xs:selector xpath="c" />  
           <xs:field xpath="@context" />  
          </xs:keyref>  
         </xs:element>  
          
        
        

        das erlaubt abre noch immer z.B:

        <c context="t">...  
        <c context="t">...
        

        also muss man Eindeutigkeitsbeschränkung für <c> definieren (keyref impliziert nicht Eindeutigkeit) :

          
         <xs:element name="root">  
          <xs:key name="KeyAB1">  
           <xs:selector xpath="a" />  
           <xs:field xpath="@name" />  
          </xs:key>  
          <xs:unique name="C">  
           <xs:selector xpath="c" />  
           <xs:field xpath="@context" />  
          </xs:unique>  
          <xs:keyref refer="KeyAB1" name="RefKeyC">  
           <xs:selector xpath="c" />  
           <xs:field xpath="@context" />  
          </xs:keyref>  
         </xs:element>  
        
        

        Man kann/soll ebenfalls noch für <d> eine Eindeutigkeitsbeschränkung machen.

          
         <xs:element name="c">  
          <xs:unique name="D">  
           <xs:selector xpath="d" />  
           <xs:field xpath="@id" />  
          </xs:unique>  
         </xs:element>  
        
        

        Die Schwierigkeit ist jetzt, wie referneziert man ein bestimmtes <b> von einem bestimmten <a> ausgehend in <c>/<d>.

        Und das geht eben nicht. Man kann noch eine Referenz setzen:

          
         <xs:element name="root">  
          <xs:key name="KeyAB1">  
           <xs:selector xpath="a" />  
           <xs:field xpath="@name" />  
          </xs:key>  
          <xs:unique name="C">  
           <xs:selector xpath="c" />  
           <xs:field xpath="@context" />  
          </xs:unique>  
          <xs:keyref refer="KeyAB1" name="RefKeyC">  
           <xs:selector xpath="c" />  
           <xs:field xpath="@context" />  
          </xs:keyref>  
          <xs:keyref refer="KeyAB2" name="RefKeyD">  
           <xs:selector xpath="c/d" />  
           <xs:field xpath="@id" />  
          </xs:keyref>  
         </xs:element>  
        
        

        ABER: weil keyref eben keine Eindeutigkeit impliziert, können hier beliebige <b>-werte referenziert werden, sie müssen nur vorhanden sein. Dadurch ist z.B. so etwas möglich:

          
         <a name="s">  
          <b name="v">..</b>  
          <b name="w">..</b>  
          <b name="x">..</b>  
         </a>  
         <a name="u">  
          <b name="x">..</b>  
          <b name="y">..</b>  
          <b name="z">..</b>  
         </a>  
         <c context="s">  
          <d id="z">..</d> <---- falsche Referenz, ist aber "gültig"  
          <d id="x">..</d>  
         </c>  
         <c context="u">  
          <d id="x">..</d>  
          <d id="y">..</d>  
         </c>  
        
        

        Was tun? Das XML anderes designen?

        Grüße
        Thomas

        Hier nochmal das Schema insgesamt:

          
        <?xml version="1.0" encoding="UTF-8"?>  
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">  
         <xs:element name="root">  
          <xs:complexType>  
           <xs:sequence>  
            <xs:element maxOccurs="unbounded" ref="a"/>  
            <xs:element maxOccurs="unbounded" ref="c"  />  
           </xs:sequence>  
          </xs:complexType>  
          <xs:key name="KeyAB1">  
           <xs:selector xpath="a" />  
           <xs:field xpath="@name" />  
          </xs:key>  
          <xs:unique name="C">  
           <xs:selector xpath="c" />  
           <xs:field xpath="@context" />  
          </xs:unique>  
          <xs:keyref refer="KeyAB1" name="RefKeyC">  
           <xs:selector xpath="c" />  
           <xs:field xpath="@context" />  
          </xs:keyref>  
          <xs:keyref refer="KeyAB2" name="RefKeyD">  
           <xs:selector xpath="c/d" />  
           <xs:field xpath="@id" />  
          </xs:keyref>  
         </xs:element>  
         <xs:element name="a">  
          <xs:complexType>  
           <xs:sequence>  
            <xs:element maxOccurs="unbounded" ref="b"/>  
           </xs:sequence>  
           <xs:attribute name="name" use="required" type="xs:NCName"/>  
          </xs:complexType>  
          <xs:key name="KeyAB2">  
           <xs:selector xpath="b"></xs:selector>  
           <xs:field xpath="@name"></xs:field>  
          </xs:key>  
         </xs:element>  
         <xs:element name="b">  
          <xs:complexType>  
           <xs:simpleContent>  
            <xs:extension base="xs:NMTOKEN">  
             <xs:attribute name="name" use="required" type="xs:NCName"/>  
            </xs:extension>  
           </xs:simpleContent>  
          </xs:complexType>  
         </xs:element>  
         <xs:element name="c">  
          <xs:complexType>  
           <xs:sequence>  
            <xs:element maxOccurs="unbounded" ref="d"/>  
           </xs:sequence>  
           <xs:attribute name="context" use="required" type="xs:NCName"/>  
          </xs:complexType>  
          <xs:unique name="D">  
           <xs:selector xpath="d" />  
           <xs:field xpath="@id" />  
          </xs:unique>  
         </xs:element>  
         <xs:element name="d">  
          <xs:complexType>  
           <xs:simpleContent>  
            <xs:extension base="xs:NMTOKEN">  
             <xs:attribute name="id" use="required" type="xs:NCName"/>  
            </xs:extension>  
           </xs:simpleContent>  
          </xs:complexType>  
         </xs:element>  
        </xs:schema>