I'm trying to create a validation schema for this sample XML. I already tried using recursive definitions, but cannot find a proper way. Please note that IF and ELSE elements can be nested any number of times, and ACTIONx elements can appear in any order. Also note that every element has its own possibly different attributes.
<?xml version="1.0" encoding="utf-8"?>
<KEYPRESS Buffer="1">
<ACTION1 Index="15" />
<IF Condition="0">
<IF Condition="1">
<ACTION1 Index="14" />
<ELSE>
<ACTION2 Measure="whatever" />
</ELSE>
</IF>
<IF Condition="2">
<IF Condition="5">
<ACTION2 Measure="whatelse" />
<ACTION3 Type="Flag" />
</IF>
<ELSE>
<ACTION1 Index="0" />
<ACTION3 Type="Other" />
<IF Condition="1">
<ACTION3 Type="Flag" />
</IF>
</ELSE>
</IF>
</IF>
</KEYPRESS>
Any help will be appreciated.
*** EDIT *** Many thanks to Michael Kay, excellent solution! I'm posting here the resulting schema, in case someone might be interested.
<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid Studio 2021 (https://www.liquid-technologies.com) -->
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="INSTRUCTION" abstract="true"/>
<xs:element name="ACTION1" substitutionGroup="INSTRUCTION">
<xs:complexType>
<xs:attribute name="Index" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="ACTION2" substitutionGroup="INSTRUCTION">
<xs:complexType>
<xs:attribute name="Index" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="ACTION3" substitutionGroup="INSTRUCTION">
<xs:complexType>
<xs:attribute name="Type" type="xs:string" use="required" />
<xs:attribute name="Index" type="xs:string" use="required" />
<xs:attribute name="Value" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="IF" substitutionGroup="INSTRUCTION">
<xs:complexType>
<xs:sequence>
<xs:element ref="INSTRUCTION" minOccurs="1" maxOccurs="unbounded"/>
<xs:element name="ELSE" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="Type" type="xs:string" use="required" />
<xs:attribute name="Index" type="xs:string" use="required" />
<xs:attribute name="Condition" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="KEYPRESS" >
<xs:complexType>
<xs:sequence>
<xs:element ref="INSTRUCTION" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="KeySeq" type="xs:string" use="required" />
<xs:attribute name="Buffer" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="ELSE" >
<xs:complexType>
<xs:sequence>
<xs:element ref="INSTRUCTION" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
This kind of structure is what substitution groups are for.
Define an abstract element INSTRUCTION
, and then define IF
, ACTION1
, ACTION2
, and ACTION3
as members of the substitution group of INSTRUCTION
, each with its own type definition defining the permitted attributes. The content model of KEYPRESS
and ELSE
appears to be INSTRUCTION*
(a sequence of 0 or more instructions), and the content model of IF
appears to be (INSTRUCTION*, ELSE?)
(a sequence of 0 or more instructions followed optionally by an ELSE
).
(ELSE
is not a member of the substitution group, because it can't appear anywhere an instruction appears).