Simple attribute implication

From W3C Wiki

Simple attribute implication (a co-constraint use case)

Make one attribute optional if and only if a specific other attribute is present, and otherwise forbidden.

Cf. Value-equals_test_required (which depends not on presence of the other attribute but on its specific value) and Attribute mutex (which depends on the absence rather than the presence of the other attribute).

source: Corey Snow [1]


Other use cases: Co-constraint Use Cases

Description

   What I'd like to be able to do is make the attribute group optional, based
   on whether one of the attributes in the group (the required one) is
   supplied -- in other words, 
   if the attrOne attribute is supplied, an attrTwo
   attribute may optionally be supplied. If attrOne is not supplied, 
   attrTwo CANNOT be supplied, but the document remains 
   valid if neither is supplied.
   So under the behavior I would like to achive:
  • `<elementOne type="sometype"/>` <== Valid
  • `<elementOne type="sometype" attrOne="val" attrTwo="otherval"/>` <==Also valid
  • `<elementOne type="sometype" attrTwo="val"/>` <== Invalid, because there is no "attrOne" attribute.

Analysis

(Add your analysis here; see your name in pixels!)

Possible solutions

Relax NG

A Relax NG solution which follows the logic shown below under 'Grammaticalization' is possible.

Grammaticalization

Using the syntax described under Grammaticalization, a solution is:

 <xsd:element name="elementOne">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:attribute name="type"/>
    <xsd:sequence minOccurs="0" maxOccurs="1">
     <xsd:attribute name="attrOne" use="required"/>
     <xsd:attribute name="attrTwo" use="optional"/>
    </xsd:sequence>
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>

Schematron

Check clause

One possible solution is:

 <xsd:element name="elementOne">
  <xsd:complexType>
   <xsd:annotation><xsd:appinfo><sch:pattern><sch:rule>
       <sch:assert test="./@attrOne or not(./@attrTwo)">
	Attribute attrTwo may occur only if attrOne occurs.
       </sch:assert>
      </sch:rule></sch:pattern></xsd:appinfo></xsd:annotation>
   <xsd:sequence/>
   <xsd:attribute name="type" use="required"/>
   <xsd:attribute name="attrOne" use="optional"/>
   <xsd:attribute name="attrTwo" use="optional"/>
  </xsd:complexType>
 </xsd:element>

(Solution proposed by MSM)

SchemaPath

Two-type solution

One SchemaPath solution is to define two distinct complex types: one with attribute attrOne, in which attrOne is required and attrTwo is optional:

 <xsd:complexType name="with_a1">
  <xsd:sequence/>
  <xsd:attribute name="type" use="required"/>
  <xsd:attribute name="attrOne" use="required"/>
  <xsd:attribute name="attrTwo" use="optional"/>
 </xsd:complexType>

and one in which neither of those two attributes occurs:

 <xsd:complexType name="sans_a1">
  <xsd:sequence/>
  <xsd:attribute name="type" use="required"/>
 </xsd:complexType>

The element declaration can then simply choose one type or the other, depending on whether attrOne occurs in the instance:

 <xsd:element name="elementOne">
  <xsd:alt cond="./@attrOne"      type="this:with_a1"/>
  <xsd:alt cond="not(./@attrOne)" type="this:sans_a1"/>
 </xsd:element>

Here each alternative has a condition, just to make the circumstances under which sans_a1 is chosen explicit. Those who less obsessively imitate the mannerisms of Edsger Dijkstra might prefer the more idiomatic form:

 <xsd:element name="elementOne">
  <xsd:alt cond="./@attrOne" type="this:with_a1"/>
  <xsd:alt                   type="this:sans_a1"/>
 </xsd:element>


Single-type solution

A second SchemaPath solution might imitate the logic of the check clause solution given above. If we assume that the difference between elements with attrOne (and optionally attrTwo) and elements without attrOne is not so important that we want it to be reflected in the [type definition] in the PSVI, then we can define a single type, here called typeOne:

 <xsd:complexType name="typeOne">
  <xsd:sequence/>
  <xsd:attribute name="type" use="required"/>
  <xsd:attribute name="attrOne" use="optional"/>
  <xsd:attribute name="attrTwo" use="optional"/>
 </xsd:complexType>

And then we check the co-constraint in an xsd:alt:

 <xsd:element name="elementOne">
  <xsd:alt type="xsd:error"    cond="./@attrTwo and not(@attrOne)"/>
  <xsd:alt type="this:typeOne" />
 </xsd:element>

(Solution proposed by MSM)

Conditional Type