XML schema for elements with same name but different sub-structure depending on context - xsd

I try to define a schema for XML documents I receive.
The documents look like:
<root>
<items>
<group name="G-1">
<item name="I-1"/>
<item name="I-2"/>
<item name="I-3"/>
<item name="I-4"/>
</group>
</items>
<data>
<group name="G-1" place="here">
<customer name="C-1">
<item name="I-1" count="3"/>
<item name="I-2" count="4"/>
</customer>
<customer name="C-2">
<item name="I-3" count="7"/>
</customer>
</group>
</data>
</root>
I tried XmlSpy and xsd.exe from .NET 2.0. Both created schema definitions which allow below <group> any number of <item> and <customer> elements. But what I'm looking for should restrict <group> below <items> to <item> elements, and <group> below <data> to <customer> elements.
Is this something xml schema is not capable at all?

The key points (see XML Schema Runtime Polymorphism via xsi:type and Abstract Types for complete and correct context/placement/usage) are:
Create a base type with (abstract="true" to prevent it from being used directly)
Note: the ref attribute replaces the name attribute for elements defined elsewhere
<xs:complexType name="CustomerType" abstract="true" >
<xs:sequence>
<xs:element ref="cust:FirstName" />
<xs:element ref="cust:LastName" />
<xs:element ref="cust:PhoneNumber" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="customerID" type="xs:integer" />
</xs:complexType>
Create two or more derived types by extending or restricting the base type
<xs:complexType name="MandatoryPhoneCustomerType" >
<xs:complexContent>
<xs:restriction base="cust:CustomerType">
<xs:sequence>
<xs:element ref="cust:FirstName" />
<xs:element ref="cust:LastName" />
<xs:element ref="cust:PhoneNumber" minOccurs="1" />
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
and
<xs:complexType name="AddressableCustomerType" >
<xs:complexContent>
<xs:extension base="cust:CustomerType">
<xs:sequence>
<xs:element ref="cust:Address" />
<xs:element ref="cust:City" />
<xs:element ref="cust:State" />
<xs:element ref="cust:Zip" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
Reference the base type in an element
<xs:element name="Customer" type="cust:CustomerType" />
In your instance XML document, specify the specific derived type as an xsi:type attribute
<cust:Customer customerID="12345" xsi:type="cust:MandatoryPhoneCustomerType" >
<cust:FirstName>Dare</cust:FirstName>
<cust:LastName>Obasanjo</cust:LastName>
<cust:PhoneNumber>425-555-1234</cust:PhoneNumber>
</cust:Customer>
or:
<cust:Customer customerID="67890" xsi:type="cust:AddressableCustomerType" >
<cust:FirstName>John</cust:FirstName>
<cust:LastName>Smith</cust:LastName>
<cust:Address>2001</cust:Address>
<cust:City>Redmond</cust:City>
<cust:State>WA</cust:State>
<cust:Zip>98052</cust:Zip>
</cust:Customer>

Yes, XSD can handle this. I generated this schema from Visual Studio 2008 (much faster than doing it by hand) and it will do what you're looking for:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="items">
<xs:complexType>
<xs:sequence>
<xs:element name="group">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="item">
<xs:complexType>
<xs:attribute name="name" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="data">
<xs:complexType>
<xs:sequence>
<xs:element name="group">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="customer">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="item">
<xs:complexType>
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="count" type="xs:unsignedByte" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="place" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

Related

how to declare multiple groups in XSD?

In my scenario my XSD should have multiple groups under one root tag,
#XSD#
<xs:group name="location">
<xs:sequence>
<xs:element name="city" type="xs:string"/>
<xs:element name="flat_num" type="xs:string"/>
<xs:element name="landmark" type="xs:string"/>
<xs:element name="street" type="xs:string"/>
</xs:sequence>
</xs:group>
<xs:group name="student">
<xs:sequence>
<xs:element name="firstname" type="xs:string"/>
<xs:element name="lastname" type="xs:string"/>
<xs:element name="mothername" type="xs:string"/>
<xs:element name="fathername" type="xs:string"/>
</xs:sequence>
</xs:group>
<xs:element name="Student_details" type="details"/>
<xs:complexType name="details">
<xs:group ref="location"/>
<xs:group ref="student"/>
</xs:complexType>
</xs:schema>
When i am validating with xml , it says this format is wrong ,
can any one help to let me know how to create multiple groups
Your missing the Sequence in the complex type
<?xml version="1.0" encoding="utf-8" ?>
<!--Created with Liquid Studio 2017 - Developer Bundle Edition (Trial) 15.0.0.7089 (https://www.liquid-technologies.com)-->
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:group name="location">
<xs:sequence>
<xs:element name="city" type="xs:string" />
<xs:element name="flat_num" type="xs:string" />
<xs:element name="landmark" type="xs:string" />
<xs:element name="street" type="xs:string" />
</xs:sequence>
</xs:group>
<xs:group name="student">
<xs:sequence>
<xs:element name="firstname" type="xs:string" />
<xs:element name="lastname" type="xs:string" />
<xs:element name="mothername" type="xs:string" />
<xs:element name="fathername" type="xs:string" />
</xs:sequence>
</xs:group>
<xs:element name="Student_details" type="details" />
<xs:complexType name="details">
<xs:sequence>
<xs:group ref="location" />
<xs:group ref="student" />
</xs:sequence>
</xs:complexType>
</xs:schema>
I would also question whether a group best implements what you are trying to describe. What you have currently will produce XML like this
<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid Studio 2017 - Developer Bundle Edition (Trial) 15.0.0.7089 (https://www.liquid-technologies.com) -->
<Student_details xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="Schema.xsd">
<city>string</city>
<flat_num>string</flat_num>
<landmark>string</landmark>
<street>string</street>
<firstname>string</firstname>
<lastname>string</lastname>
<mothername>string</mothername>
<fathername>string</fathername>
</Student_details>

Can xsd parent element (base) be given default value in child (extension/restriction)?

I'm trying to do something like this:
<xs:complexType name="tSomeComponent">
<xs:sequence>
<xs:element name="table" type="xs:string" />
<xs:element name="key" type="tKey" />
<xs:element name="class" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="tStation">
<xs:complexContent>
<xs:restriction base="tSomeComponent">
<xs:sequence>
<xs:element name="table" type="xs:string" default="station" />
<xs:element name="name" type="xs:string" />
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
I know that table default value in child should be done by restriction but i want to extend and restrict at same time. I know that this can be solved by adding more types but that's an overkill.

xsd error in the following

here is the xml
<?xml version="1.0" encoding="utf-8"?>
<Modules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="XSDQu3.xsd">
<Module code="CSE1246">
<Name shortName="ADSA">Applied Data Structures and Algorithms</Name>
<Level>1</Level>
<ResourcePerson>
<Name>Anwar</Name>
<Surname>Chutoo</Surname>
</ResourcePerson>
</Module>
<Module code="CSE2041">
<Name shortName="Web 2">Web Technologies II</Name>
<Level>2</Level>
<ResourcePerson>
<FullName>Shehzad Jaunbuccus</FullName>
</ResourcePerson>
</Module>
</Modules>
i'm having an error at name. a resource person can either contain fullname or name and surname. please help. Am i correctly doing this part
here is the xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="NameNSurnameType">
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Surname" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ResourcePersonType">
<xs:sequence>
<xs:choice>
<xs:element name="NameNSurnameType" type="NameNSurnameType"/>
<xs:element name="FullName" type="xs:string"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:attribute name="code">
<xs:simpleType>
<xs:restriction base="xs:ID">
<xs:pattern value="CSE(\d{4})"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:complexType name="nameType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="shortName" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:element name="Modules">
<xs:complexType>
<xs:sequence>
<xs:element name="Module" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" type="nameType"/>
<xs:element name="Level" type="xs:positiveInteger"/>
<xs:element name="ResourcePerson" type="ResourcePersonType"/>
</xs:sequence>
<xs:attribute ref="code" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
I can see your confusion; a choice "option" can be almost any particle; a compositor (such as xs:sequence or another xs:choice) would match the description. The minimum you need to do is change the following definition.
<xs:complexType name="ResourcePersonType">
<xs:sequence>
<xs:choice>
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Surname" type="xs:string"/>
</xs:sequence>
<xs:element name="FullName" type="xs:string"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
If you want to reference the name as you seem to want to do it with the global complex type, then one can create a group and reference that instead. Below is a modified XSD consistent with the above scenario:
<?xml version="1.0" encoding="utf-8" ?>
<!-- XML Schema generated by QTAssistant/XSD Module (http://www.paschidev.com) -->
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:group name="NameNSurnameType">
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Surname" type="xs:string"/>
</xs:sequence>
</xs:group>
<xs:complexType name="ResourcePersonType">
<xs:sequence>
<xs:choice>
<xs:group ref="NameNSurnameType"/>
<xs:element name="FullName" type="xs:string"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:attribute name="code">
<xs:simpleType>
<xs:restriction base="xs:ID">
<xs:pattern value="CSE(\d{4})"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:complexType name="nameType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="shortName" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:element name="Modules">
<xs:complexType>
<xs:sequence>
<xs:element name="Module" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" type="nameType"/>
<xs:element name="Level" type="xs:positiveInteger"/>
<xs:element name="ResourcePerson" type="ResourcePersonType"/>
</xs:sequence>
<xs:attribute ref="code" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

XSD Schema that JAXB would find acceptable

I have a REST XML response that has a chunk of data like this:
<tag>
<total-pages type="integer">5</total-pages>
<previous-page nil="true"></previous-page>
<next-page nil="true"></next-page>
<offset type="integer">5</offset>
</tag>
Now, sometimes the data can come back like this:
<tag>
<total-pages type="integer">5</total-pages>
<previous-page type="integer">0</previous-page>
<next-page type="integer">1</next-page>
<offset type="integer">5</offset>
</tag>
I have been trying to come up with a XSD schema structure that will account for both possibilities that would be acceptable to JAXB.
I have tried:
<tag>
<total-pages type="numeric-type" />
<previous-page type="numeric-type" />
<next-page type="numeric-type" />
<offset type="numeric-type" />
</tag>
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:integer">
<xsd:attribute type="xsd:string" name="type" use="optional">
<xsd:attribute type="xsd:boolean" name="nil" use="optional">
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
but JAXB blows up with an unmarshalling error.
Thoughts on what XSD schema structure I could use to account for the variability in the returning XML (I cannot change the XML, it is coming from a third-party which I have no control).
Thank you,
Perry
I've started by generating one XSD from your XML samples (two in your case):
<?xml version="1.0" encoding="utf-8"?>
<!--XML Schema generated by QTAssistant/XML Schema Refactoring (XSR) Module (http://www.paschidev.com)-->
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="tag">
<xs:complexType>
<xs:sequence>
<xs:element name="total-pages">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:unsignedByte">
<xs:attribute name="type" type="xs:string" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="previous-page">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="type" type="xs:string" use="optional" />
<xs:attribute name="nil" type="xs:boolean" use="optional" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="next-page">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="type" type="xs:string" use="optional" />
<xs:attribute name="nil" type="xs:boolean" use="optional" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="offset">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:unsignedByte">
<xs:attribute name="type" type="xs:string" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
When you compare with your XSD, you'll notice the use of string instead of integer as the base type. Which explains why you have a problem with unmarshalling: an empty string is not a valid number.
If instead of "nil" you would get an xsi:nil attribute, the XSD would've been generated differently, i.e. it would've had nillable="true" for your elements.
Since you cannot change the XML, then you're stuck with the use of string; alternatively, you could create a union between an empty string and a numeric value... as to how that'll look in JAXB, you can try it...

Problem with xml schema elements hierarchy

What's wrong with this xml schema? It doesn't parse correctly, and I can't realize a hierarchy between cluster(element)->host(element)->Load(element).
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="cluster">
<xs:complexType>
<xs:sequence>
<xs:element ref="host"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="host">
<xs:complexType>
<xs:element ref="Load"/>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="Load">
<xs:complexType>
<xs:attribute name="usedPhisicalMemory" type="xs:integer"/>
</xs:complexType>
</xs:element>
</xs:schema>
Thank you, Emilio
To allow something like this (I corrected the typo in "usedPhysicalMemory"):
<cluster>
<host name="foo">
<Load usedPhysicalMemory="500" />
</host>
<host name="bar">
<Load usedPhysicalMemory="500" />
</host>
</cluster>
This schema would do it:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="cluster">
<xs:complexType>
<xs:sequence>
<xs:element ref="host" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="host">
<xs:complexType>
<xs:sequence>
<xs:element ref="Load" />
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="Load">
<xs:complexType>
<xs:attribute name="usedPhysicalMemory" type="xs:integer" />
</xs:complexType>
</xs:element>
</xs:schema>
From the MSDN on <xs:complexType> (because the spec makes my brain hurt):
If group, sequence, choice, or all is specified, the elements must
appear in the following order:
group | sequence | choice | all
attribute | attributeGroup
anyAttribute
Maybe someone else can point out the relevant section in the spec.
In the host element, the load element cannot be a child of complexType, you must have a sequence, etc. in between.

Resources