Search code examples
xmlxsdkeyref

Optional keyref in xsd?


Is it possible to define an attribute in xsd so that it may have an empty value but is checked to refer to another element when there is a value?

<?xml version="1.0" encoding="UTF-8"?>
<TableStructure xmlns="tmp/TableStructure"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="tmp/TableStructure TableStructure.xsd">
  <Column name="a" />
  <Column name="b" />
  <Column name="c" />
  <Range name="r1" fromColumn="a" toColumn="b" /> <!-- works -->
  <Range name="r2" fromColumn="a" toColumn="" /> <!-- should work -->
  <Range name="r3" fromColumn="a" /> <!-- works -->
  <Range name="r4" fromColumn="" toColumn="c" /> <!-- should work -->
  <Range name="r5" fromColumn="foo" toColumn="bar" /> <!-- should not and does not work -->
</TableStructure>

I tried the following Schema:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="tmp/TableStructure" elementFormDefault="qualified"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="tmp/TableStructure"
            xmlns:ts="tmp/TableStructure">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
                schemaLocation='http://www.w3.org/2009/01/xml.xsd' />

    <xsd:element name="TableStructure">
        <xsd:complexType>
            <xsd:sequence minOccurs="1" maxOccurs="1">
                <xsd:element ref="Column" maxOccurs="unbounded" minOccurs="0" />
                <xsd:element ref="Range" maxOccurs="unbounded" minOccurs="0" />
            </xsd:sequence>
        </xsd:complexType>
        <xsd:key name="columnName-key">
            <xsd:selector xpath="./ts:Column" />
            <xsd:field xpath="@name" />
        </xsd:key>
        <xsd:keyref name="fromColumn-ref" refer="columnName-key">
            <xsd:selector xpath="./ts:Range" />
            <xsd:field xpath="@fromColumn" />
        </xsd:keyref>
        <xsd:keyref name="toColumn-ref" refer="columnName-key">
            <xsd:selector xpath="./ts:Range" />
            <xsd:field xpath="@toColumn" />
        </xsd:keyref>
    </xsd:element>

    <xsd:element name="Column">
        <xsd:complexType>
            <xsd:attribute name="name" type="xsd:string" use="required" />
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Range">
        <xsd:complexType>
            <xsd:attribute name="name" type="xsd:string" use="required" />
            <xsd:attribute name="fromColumn" use="optional" type="xsd:string" />
            <xsd:attribute name="toColumn" use="optional" type="xsd:string" />
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

Can I use the selector/field to somehow exclude empty attributes?

I'm trying to create a schema for existing files and would prefer not to have to change them to remove empty attributes.


Solution

  • No, the keyref feature doesn't allow you to treat attributes whose value is a zero-length string differently from other attributes.

    XSD tends to be a bit paternalistic in that it only allows you to describe designs that the XSD designers considered to be good practice, and I imagine that the designers of XSD would be telling you that you should omit the attribute here rather than having it present with an empty value.