XPath assertion in an xsd schema

Here is an example a bit simplified but similar to your needs, with a list type and an assertion:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
    xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1">
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="parts"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="parts">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded" ref="part"/>
            </xs:sequence>
            <xs:assert id="test-reference-id" test="every $part in part satisfies every $id in $part/data(@next) satisfies some $part2 in part satisfies $id = $part2/@id"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="part">
        <xs:complexType>
            <xs:attribute name="id" use="required" type="xs:string"/>
            <xs:attribute name="next" type="id-list"/>
        </xs:complexType>
    </xs:element>
    <xs:simpleType name="id-list">
        <xs:list itemType="xs:string"></xs:list>
    </xs:simpleType>
</xs:schema>

The example

<root>
    <parts>
        <part id="p1" next="p2 p4"/>
        <part id="p2" next="p3 p4 p5"/>
        <part id="p3" next="p4"/>
        <part id="p4"/>
    </parts>
</root>

then fails the assertion while

<root>
    <parts>
        <part id="p1" next="p2 p4"/>
        <part id="p2" next="p3 p4"/>
        <part id="p3" next="p4"/>
        <part id="p4"/>
    </parts>
</root>

is valid.

My attempt to implement your assertions is

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace = "http://www.ludd21.com/kPartModel"
    xmlns = "http://www.ludd21.com/kPartModel"
    elementFormDefault="qualified" 
    vc:minVersion = "1.1"
    xpathDefaultNamespace="##targetNamespace"
    xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
    >   
    <xs:element name="kPartModel">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="kPartsPiece"  minOccurs="1" maxOccurs = "unbounded"/>
            </xs:sequence>
            <xs:attribute name="modelName" type="xs:NCName" use = "required"/>
        </xs:complexType>
        <!--piecename must be unique within kpModel-->
        <xs:unique name= "kPartModel">
            <xs:selector xpath="*"/>
            <xs:field xpath= "@pieceName"/>
        </xs:unique>
    </xs:element>
    
    <xs:element name="kPartsPiece">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref= "kPartsList"/>
            </xs:sequence>         
            <xs:attribute name="pieceName" type="xs:NCName"/>
            <!-- if they exist previousnum and nextsnum must contain valid partNumber that is referring to a part in kpartsList-->
            <xs:assert test = "every $x in data(kPartsList/*/@previousnum) satisfies some $part in kPartsList/* satisfies $part/@partNumber = $x"/>
            <xs:assert test = "every $x in data(kPartsList/*/@nextsnum) satisfies some $part in kPartsList/* satisfies $part/@partNumber = $x"/>
        </xs:complexType>
        <!-- @partNumber is unique across kPartsList -->
        <xs:unique name= "kPartsList">
            <xs:selector xpath="*/*"/>
            <xs:field xpath= "@partNumber"/>
        </xs:unique>
    </xs:element>
    
    <xs:element name = "kPartsList" >
        <xs:complexType>
            <xs:sequence>
                <xs:choice >
                    <xs:element ref = "castOnPartSeg"/>
                    <xs:element ref = "castOnPartPoint"/>
                </xs:choice>
                <xs:choice  minOccurs= "0" maxOccurs = "unbounded">
                    <xs:element ref = "castOnPartSeg" />
                    <xs:element ref = "castOnPartPoint" />
                    <xs:element ref = "castOffPartSeg" />
                    <xs:element ref = "castOffPartPoint"/>
                    <xs:element ref = "joinPart"/>
                    <xs:element ref = "splitPart" />
                    <xs:element ref = "segSplitPart"/>  
                </xs:choice>
            </xs:sequence>        
        </xs:complexType>      
    </xs:element>
    
    
    <xs:element name="castOnPartPoint">
        <xs:complexType>
            <xs:attribute name ="nextsnum" type = "kpRefsList" use = "required"/>
            <xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
            <xs:attribute name = "heightd" type = "decimal5digits" use = "required"/>
        </xs:complexType>
    </xs:element>
    
    <xs:element name="castOnPartSeg">
        <xs:complexType>
            <xs:attribute name = "nextsnum"  type = "kpRefsList" use = "required"/>
            <xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
            <xs:attribute name = "heightd" type = "decimal5digits" use = "required"/>
        </xs:complexType>
    </xs:element>
    
    
    <xs:element name="castOffPartPoint">
        <xs:complexType>
            <xs:attribute name ="previousnum" type = "kpRefsList" use = "required"/> 
            <xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
            <xs:attribute name = "heightd" type = "decimal5digits" fixed = "0" use = "required"/>
        </xs:complexType>
    </xs:element>
    
    
    <xs:element name="castOffPartSeg">
        <xs:complexType>
            <xs:attribute name ="previousnum" type = "kpRefsList" use = "required"/> 
            <xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
            <xs:attribute name = "heightd" type = "decimal5digits" fixed="0" use = "required"/>
        </xs:complexType> 
    </xs:element>
    
    
    <xs:element name="splitPart">
        <xs:complexType>
            <xs:attribute name ="previousnum" type = "kpRefsList" use = "required"/> 
            <xs:attribute name ="nextsnum" type = "kpRefsList" use = "required"/>
            <xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
            <xs:attribute name = "heightd" type = "decimal5digits" use = "required"/>
        </xs:complexType>
    </xs:element>
    
    <xs:element name="segSplitPart">
        <xs:complexType>
            <xs:attribute name ="previousnum"  type = "kpRefsList" use = "required"/>        
            <xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
            <xs:attribute name = "heightd" type = "decimal5digits" fixed= "0" use = "required"/>       
        </xs:complexType>
    </xs:element>
    
    <xs:element name="joinPart">
        <xs:complexType>
            <xs:attribute name ="nextsnum"  type = "kpRefsList" use = "required"/>   
            <xs:attribute name ="previousnum" type = "kpRefsListmin2" use = "required"/>
            <xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
            <xs:attribute name = "heightd" type = "decimal5digits" use = "required"/>      
        </xs:complexType>
    </xs:element>
    
    <xs:simpleType name = "kpRefsList">
        <xs:list itemType= "xs:nonNegativeInteger"/>
    </xs:simpleType>
    
    <xs:simpleType name="kpRefsListmin2"> 
        <xs:restriction base = "kpRefsList"> 
            <xs:minLength value = "2"/>
        </xs:restriction>
    </xs:simpleType>
    
    
    <xs:simpleType name ="decimal5digits">
        <xs:restriction base = "xs:decimal">
            <xs:fractionDigits value="5"/>
        </xs:restriction>
    </xs:simpleType>    
</xs:schema>

Leave a Comment