Simple attribute implication
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)