Question

I wish to create an XML document in which one of its elements must be defined with one of two known attributes, but not both.

For example, I would like to define a "webAddress" element as follows:

 <xs:element name="webAddress">
     <xs:complexType>
         <xs:attribute name="hostName" type="xs:string"/>
         <xs:attribute name="hostAddress" type="xs:string"/>
     </xs:complexType>
 </xs:element>

but I only want a user to be able to define either a hostName attribute (e.g., hostName= ") or a hostAddress (e.g., hostAddress="") attribute, but not both. It appears that the construct accomplishes this for elements. Is there a functional equivalent for attributes, or is there a better way to approach this?

If found that we can't do this with W3C XML Schema. We can use an embedded schematron rule but can we do it on choice element by checking element and attribute together? For example something like this:

<xs:choice>
     <xs:element name="webAddress">
         <xs:complexType>
             <xs:attribute name="hostName" type="xs:string"/>
         </xs:complexType>
     </xs:element>
     <xs:element name="webAddress">
         <xs:complexType>
             <xs:attribute name="hostAddress" type="xs:string"/>
         </xs:complexType>
     </xs:element>
  </xs:choice>
Was it helpful?

Solution

You could use "type" substitution, e.g.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <!-- Some abstract type - it might define some common parts too. It is abstract and cannot be used directly for instance -->
    <xs:complexType name="abstractType" abstract="true"/>

    <!-- First "children" type of abstract type-->
    <xs:complexType name="hostNameType">
        <xs:complexContent>
            <!-- it is derived from abstractType and adds attribute hostName -->
            <xs:extension base="abstractType">
                <xs:attribute name="hostName" type="xs:string" use="required" />
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <!-- Second "children" type of abstract type -->
    <xs:complexType name="hostAddressType">
        <xs:complexContent>
            <!-- it is also derived from abstractType and adds attribute hostAddress -->
            <xs:extension base="abstractType">
                <xs:attribute name="hostAddress" type="xs:string" use="required" />
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <!-- Element of abstractType -->
    <xs:element name="webAddress" type="abstractType" />

</xs:schema>

In instance document you have to specify type which is actually used:

<?xml version="1.0" encoding="UTF-8"?>
<!-- by xsi:type you defined which "concrete" type is really used -->
<webAddress xsi:type="hostNameType" hostName="String" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>

or

<?xml version="1.0" encoding="UTF-8"?>
<webAddress xsi:type="hostAddressType" hostAddress="xxxx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>

But the definition is a bit verbose and also it is not obvious on the first sight what type "webAddress" really could be. But it could be a way for you.

OTHER TIPS

It's easy to achieve what you want in XSD 1.1, using either assertions or conditional type assignment, but it's not possible in XSD 1.0.

XSD 1.1 is currently supported by Saxon, Xerces, and Altova.

You can achieve this with a XSD key:

<xs:element name="webAddress">
    <xs:complexType>
        <xs:attribute name="hostName" type="xs:string" use="optional" />
        <xs:attribute name="hostAddress" type="xs:string" use="optional" />
    </xs:complexType>

    <!-- Validate either name or address attribute specified (but not both). -->
    <xs:key name="WebAddressHostNameOrAddressAttributePresent">
        <xs:selector xpath="." />
        <xs:field xpath="@hostName | @hostAddress" />
    </xs:key>
</xs:element>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top