xmlxsdxmlschema

Inline children of an element, if there's only one elements of that type


Suppose I want to model a <Product>. A product can either have variants (e.g. a T-shirt that comes in different colours), or have a single variant, like a sweater that only comes in one colour.

I want to make a schema that allows for both of these syntaxes to be valid:

<MyXMLDoc>

<Product>
  <Variants>  
    <Variant>
      <Name>Red T-Shirt</Name>
      <Description>A red t-shirt</Description>
    </Variant>

    <Variant>
      <Name>Green T-Shirt</Name>
      <Description>A green t-shirt</Description>
    </Variant>

    <Variant>
      <Name>Blue T-Shirt</Name>
      <Description>A blue t-shirt</Description>
    </Variant>

  </Variants>
</Product>

<Product>
  <Name>Black sweater</Name>
  <Description>A plain ol' black sweater</Description>
</Product>

</MyXMLDoc>

The closest I could get was:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="MyXMLDoc">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded" ref="Product"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="Product">
        <xs:complexType>
            <xs:choice>
                <xs:element ref="Variant"/>
                <xs:element ref="Variants"/>
            </xs:choice>
        </xs:complexType>
    </xs:element>

    <xs:element name="Variants">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded" ref="Variant"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="Variant">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="Name"/>
                <xs:element ref="Description"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="Name" type="xs:string"/>
    <xs:element name="Description" type="xs:string"/>
</xs:schema>

The xs:choice between a <Variant> or <Variants> allows me to get rid of the <Variants> level of nesting, but the <Name> and <Description> still need to be parented under a single <Variant>.

Is there a way to tell an XML-schema to expect either an element (<Variant>), or the in-lined members of an element (the <Name>, <Description> from within a <Variant>, without needing to actually be wrapped in a <Variant>)? Notice that the <Name> and `


Solution

  • I think this is what you are aiming at:

    <?xml version="1.0" encoding="utf-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
        <xs:element name="MyXMLDoc">
            <xs:complexType>
                <xs:sequence>
                    <xs:element maxOccurs="unbounded" ref="Product"/>
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    
        <xs:element name="Product">
            <xs:complexType>
                <xs:choice>
                    <xs:group ref="VariantGroup"/>
                    <xs:element ref="Variant"  maxOccurs="unbounded"/>
                </xs:choice>
            </xs:complexType>
        </xs:element>
    
        <xs:element name="Variant">
            <xs:complexType>
                <xs:group ref="variantGroup"/> 
            </xs:complexType>
        </xs:element>
    
        <xs:group name="VariantGroup">
            <xs:sequence>
                <xs:element name="Name" type="xs:string"/>
                <xs:element name="Description" type="xs:string"/>
            </xs:sequence>
        </xs:group>
    
    </xs:schema>
    

    I have removed the <Variants> level because your comments seem to indicate that you want to avoid it - although your XML example does include it.

    Although your suggested XML can be described using XML Schema, I think it would be simpler to represent the single-variant product like this:

    <Product>
      <Variant>
        <Name>Black sweater</Name>
        <Description>A plain ol' black sweater</Description>
      </Variant>
    </Product>
    

    That would make the XML Schema much simpler - just set minOccurs=1 on the Variant element.