Search code examples
xmlxsdxsd-validationxml-validation

XML not validating against XSD with key and keyref defined


Having trouble getting an XML file to validate against an XSD. Am using Visual Studio 2022 to edit and test the XML and XSD.

Trying to implement validation that a Package in a Workload is one of the known available Packages.

XSD itself validates, but when trying to validate the XML with the XSD, am getting an error from the WorkloadPackage element's Keyref constraint.

The problem lies in the key and/or keyref xpath selector specifications, I believe.

Thanks for helping!!

The error received is:

  • Warning The Keyref 'http://widgetcorp.com/test.xsd:WorkloadPackage' cannot find the referred key or unique in scope. config E:\kode\test.xml 7

XSD (test.xsd):

<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="http://widgetcorp.com/test.xsd"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"   
    xmlns:ugpk="http://widgetcorp.com/test.xsd"
    elementFormDefault="qualified"
>
    <xs:element name="UbergenPackages">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="ugpk:Workloads" />
                <xs:element ref="ugpk:Packages" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
                
    <xs:element name="Workloads">
        <xs:annotation>
            <xs:documentation>
                Workloads
            </xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="ugpk:Workload" minOccurs="1" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="Workload">
        <xs:annotation>
            <xs:documentation>
                Workload
            </xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="ugpk:WorkloadPackage" minOccurs="1" maxOccurs="unbounded" />
            </xs:sequence>
            <xs:attribute   name="name"     type="xs:string"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="WorkloadPackage">
        <xs:annotation>
            <xs:documentation>
                Workload Package
            </xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:attribute name="name"       type="xs:IDREF"             use="required"/>
            <xs:attribute name="desc"       type="xs:string"            use="optional" />
            <xs:attribute name="loader"     type="xs:string"            use="optional" />
            <xs:attribute name="variable"   type="xs:string"            use="optional" />
        </xs:complexType>
        <xs:keyref name="packageRef" refer="ugpk:PackageKey">
            <xs:selector xpath="ugpk:Workloads/ugpk:Workload/ugpk:WorkloadPackage"/>
            <xs:field xpath="@name"/>
        </xs:keyref>
    </xs:element>

    <xs:element name="Packages">
        <xs:annotation>
            <xs:documentation>
                Packages
            </xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="ugpk:Package" minOccurs="1" maxOccurs="unbounded"></xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="Package">
        <xs:annotation>
            <xs:documentation>
                Package Details.  With mapping to OS and release/version.
            </xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Module" minOccurs="1" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:attribute name="osname" type="xs:string"    use="required"/>
                        <xs:attribute name="packageid" type="xs:string" use="required"/>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
            <xs:attribute name="name" type="xs:ID"  use="required"/>
        </xs:complexType>
        <xs:key name="PackageKey">
            <xs:selector xpath="ugpk:Packages/ugpk:Package"/>
            <xs:field xpath="@name"/>
        </xs:key>
    </xs:element>

    <!-- Package Name -->
    <xs:simpleType name="PackageName">
        <xs:annotation>
            <xs:documentation>
                Package Name
            </xs:documentation>
        </xs:annotation>
        <xs:restriction base="xs:string">
            <xs:pattern value="([A-Za-z0-9][-_A-Za-z0-9]+)"/>
        </xs:restriction>
    </xs:simpleType>


</xs:schema>

XML (test.xml):

<?xml version="1.0" encoding="utf-8"?>
<UbergenPackages xmlns="http://widgetcorp.com/test.xsd"
               xmlns:ugpk="http://widgetcorp.com/test.xsd" >

    <Workloads>
        <Workload name="prerequisites">
            <WorkloadPackage name="aanotify"                                desc="Virtualization"/>
            <WorkloadPackage name="aatools"                                 desc="Network"/>
        </Workload>
        <Workload name="libreoffice">
            <WorkloadPackage name="libreoffice"     variable="true"         desc="Libre Office" />
        </Workload>
    </Workloads>

    <Packages>
        <Package name="aatools">
            <Module osname="Debian"         packageid="apparmor-easyprof" />
            <Module osname="Ubuntu"         packageid="apparmor-easyprof" />
        </Package>
        <Package name="aanotify">
            <Module osname="Debian"         packageid="aanotify" />
            <Module osname="Ubuntu"         packageid="aanotify" />
        </Package>
        <Package name="libreoffice">
            <Module osname="Debian"         packageid="aautils" />
            <Module osname="Ubuntu"         packageid="aautils-new" />
        </Package>
    </Packages>
    
</UbergenPackages>  

Solution

  • The rules for what happens when the key and keyref appear on different elements are just horrendously complicated and it would take me an hour or two to remind myself of the detail: it's something that's really best avoided if you possibly can. And you nearly always can.

    For this example I would put both the key and the keyref on the top level element, UbergenPackages, and define the selectors and fields relative to that origin.

    Certainly both your selectors are wrong, because they're not selecting from the element on which they are defined.