I am trying to create a xsd for the following XML that needs to be created and provided to some legacy code. The product is already out the door and I do not have the option to change the definition of the xml. The goal is to keep compatibility creating unit tests to ensure schema conforms to the legacy expectations. (Note: I am fairly new to creating xsd but have used them a lot in the past).
The system must create one of the following simplified structures , for an error condition it expects:
<Customer>
<CustomerNumber>A12</CustomerNumber>
<ErrorText>Some kind of error description</ErrorText>
</Customer>
When the customer was found successfully it expects:
<Customer>
<CustomerNumber>A12</CustomerNumber>
<Name>data</Name>
<Address>data</Address>
<City>data</City>
</Customer>
I have tried various approaches to the xsd, below is my last attempt using groups. Each time I essentially end up with Multiple definition of element 'CustomerNumber' causes the content model to become ambiguous. A content model must be formed such that during validation of an element ...
One solution is to break this into two separate xsd files and pick the appropriate one to use in the unit test validation, before I do that is there a way to have optional elements inside the top level element?
<xs:group name="missinggroup">
<xs:sequence>
<xs:element name="CustomerNumber" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="ErrorText" type="xs:string" minOccurs="1" maxOccurs="1" />
</xs:sequence>
</xs:group>
<xs:group name="foundgroup">
<xs:sequence>
<xs:element name="CustomerNumber" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="Name" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="Address" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="City" type="xs:string" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:group>
<xs:element name="Customer">
<xs:complexType>
<xs:choice>
<xs:group ref="missinggroup" />
<xs:group ref="foundgroup" />
</xs:choice>
</xs:complexType>
</xs:element>
Another alternative already tried was:
<xs:element name="Customer">
<xs:complexType>
<xs:choice>
<xs:element name="CustomerNumber" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:group ref="missinggroup" />
<xs:group ref="foundgroup" />
</xs:choice>
</xs:complexType>
</xs:element>
You have almost solved this. If you look at your types, you can see the cause of the problem. These are ambiguous because there is nothing either group that would allow a processor to see which path is correct by looking at the first element, hence the error.
Now, in both choices <CustomerNumber> is required so that can be removed from the choices. If look it at that way, you can treat this as a sequence of <CustomerNumber> followed by a choice. That can be built in XML Schema as:
<xs:element name="Customer">
<xs:complexType>
<xs:sequence>
<xs:element name="CustomerNumber" type="xs:string"/>
<xs:choice>
<xs:element name="ErrorText" type="xs:string"/>
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Address" type="xs:string"/>
<xs:element name="City" type="xs:string"/>
</xs:sequence>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
The defaults for minOccurs and maxOccurs are both 1 so I've removed those for clarity. You also probably want <Name>, <Address> and <City> to occur once otherwise you are allowing a choice where only <CustomerNumber> occurs so I've set those to 1 too.
This is the simplest schema that satisfies your requirement. In the real world, I would probably write multiple top level definitions and reference them as it makes for a more maintainable schema but that might not be an issue for you. I prefer something like:
<xs:element name="Customer">
<xs:complexType>
<xs:sequence>
<xs:element ref="CustomerNumber"/>
<xs:choice>
<xs:element ref="ErrorText"/>
<xs:group ref="AddressGroup"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="CustomerNumber" type="xs:string"/>
<xs:element name="ErrorText" type="xs:string"/>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Address" type="xs:string"/>
<xs:element name="City" type="xs:string"/>
<xs:group name="AddressGroup">
<xs:sequence>
<xs:element ref="Name"/>
<xs:element ref="Address"/>
<xs:element ref="City"/>
</xs:sequence>
</xs:group>
That's more reusable and more maintainable but if you are working with a legacy application then change is less likely and you could use the simple version.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With