Reputation: 12327
I am new to XML / XSD so this might be a simple question for most of you but I just can't find a working solution for the following problem.
I have a xml like:
<?xml version="1.0" encoding="UTF−8"?>
<gallery>
<character >
<name>Luke Skywalker</name>
<species>Human</species>
<language>Basic</language>
<home>Tatooine</home>
</character>
<character >
<name>Chewbacca</name>
<species>Wookie</species>
<language>Shyriiwook</language>
<home>Kashyyyk</home>
<battlecry>AARGH!</battlecry>
</character>
</gallery>
it is a gallery where the 2nd character has the extra element battlecry (its a wookie element the other element is a starWars type)
I have a XSD where I define the wookieType and starWarsEntity
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="starWarsEntity">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="species" type="xsd:string"/>
<xsd:element name="language" type="xsd:string"/>
<xsd:element name="home" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="wookieType">
<xsd:complexContent>
<xsd:extension base="starWarsEntity">
<xsd:sequence>
<xsd:element name="battlecry" type="xsd:string"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="character">
<xsd:choice>
<xsd:element name="wookieType" type="wookieType" />
<xsd:element name="starWarsEntity" type="starWarsEntity"/>
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="gallery">
<xsd:sequence>
<xsd:element name="character" type="character" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="gallery" type="gallery">
</xsd:element>
</xsd:schema>
What I want is a gallery which contains characters which can be of wookyType or starWarsEntity type
I validated both files online on link you can see both files there but I get some errors:
Invalid content was found starting with element 'name'. One of '{wookieType, starWarsEntity}' is expected.
What did I do wrong, both files are valid?
Upvotes: 1
Views: 1217
Reputation: 25034
Your validator is issuing an error message telling you that the document is not valid; why do you conclude from this that "both files are valid?"?
Your schema says that a 'character' element can have either of two children: starWarsEntity
or wookietype
. If you want to make your XML valid against your schema, you can change the XML to read:
<gallery>
<character >
<starWarsEntity>
<name>Luke Skywalker</name>
...
</starWarsEntity>
</character>
<character >
<wookieType>
<name>Chewbacca</name>
...
<battlecry>AARGH!</battlecry>
</wookieType>
</character>
</gallery>
If you want a schema that accepts your XML as it sits, a simpler way to do it would be to define character
with a type in which the battlecry is optional:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="character">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="species" type="xsd:string"/>
<xsd:element name="language" type="xsd:string"/>
<xsd:element name="home" type="xsd:string"/>
<xsd:element name="battlecry" type="xsd:string"
minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="character" type="character"/>
<xsd:complexType name="gallery">
<xsd:sequence>
<xsd:element ref="character" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="gallery" type="gallery"/>
</xsd:schema>
If you want to ensure that all wookies, and only wookies, have a battlecry, the simplest approach is to distinguish wookies from other characters in their element name. Let us assume that wookie
elements describe wookie characters, and 'character' elements describe non-wookie characters.
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="starWarsEntity">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="species" type="xsd:string"/>
<xsd:element name="language" type="xsd:string"/>
<xsd:element name="home" type="xsd:string"/>
<xsd:element name="battlecry" type="xsd:string"
minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="wookie-character">
<xsd:complexContent>
<xsd:restriction base="starWarsEntity">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="species" type="xsd:string">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="wookie">
<xsd:annotation>
<xsd:documentation>
For a wookie character, the species element conveys no new
information at all; we already know the character's species
from the name of the element. But we retain the species
element in order to keep 'wookie' and 'character' parallel
in structure.
</xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="language" type="xsd:string"/>
<xsd:element name="home" type="xsd:string"/>
<xsd:element name="battlecry" type="xsd:string"
minOccurs="1"/>
</xsd:sequence>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="non-wookie-character">
<xsd:complexContent>
<xsd:restriction base="starWarsEntity">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="species">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:pattern value="()|[^w].*|.[^o].*|..[^o].*|...[^k}.*|....[^i].*|.....[^e].*">
<xsd:annotation>
<xsd:documentation>
For a non-wookie character, we allow any species except 'wookie'.
</xsd:documentation>
</xsd:annotation>
</xsd:pattern>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="language" type="xsd:string"/>
<xsd:element name="home" type="xsd:string"/>
<xsd:element name="battlecry" type="xsd:string"
maxOccurs="0"/>
</xsd:sequence>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="gallery">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="character" type="non-wookie-character"/>
<xsd:element name="wookie" type="wookie-character"/>
</xsd:choice>
</xsd:complexType>
<xsd:element name="gallery" type="gallery"/>
</xsd:schema>
XSD's core design decides which type to apply to an element based on its name, possibly qualified by the names and types of its ancestors. If you design your XML to work with that approach, and not against it, XSD will be a lot easier to use.
In XSD 1.1, it is possible to use assertions to ensure that every character element with species = 'wookie' has a battlecry, or to check other kinds of redundancy in the input. But the availability of assertions does not mean it is suddenly a good idea to violate the DRY principle.
Upvotes: 2
Reputation: 23637
You declared that gallery
contains a sequence of character
. That's correct.
But then you declared that character
is a choice of either <wookieType>
or <starWarsEntity>
. The error says that a character
element cannot contain a name
element, which is true, according to your schema.
The way you declared it in the schema, your document will only validate if you use it like this:
<gallery>
<character >
<starWarsEntity>
<name>Luke Skywalker</name>
<species>Human</species>
<language>Basic</language>
<home>Tatooine</home>
</starWarsEntity>
</character>
<character >
<wookieType>
<name>Chewbacca</name>
<species>Wookie</species>
<language>Shyriiwook</language>
<home>Kashyyyk</home>
<battlecry>AARGH!</battlecry>
</wookieType>
</character>
</gallery>
Upvotes: 3