How can I specify an XML schema for an instance document like this:
In VS, reports an error: Elements with the same name and in the same scope must have the same type.
I think XSD can't fullfill your requirement, you can try to solve it from another direction.for example, use xslt to validate XML. XSLT is xpath based and rule based, it can check every places in xml.
XSD + XSLT is a good solution, XSD for schema check, XSLT for information check.
NOTE this answer is incorrect, as Serge pointed out.
Testing with xerces gives this error: type.xsd:3:21: cos-element-consistent: Error for type '#AnonType_productinfo'. Multiple elements with name 'informationset', with different types, appear in the model group.
There's more detail in the spec for cos-element-consistent.
But there's a solution, similar to Marc's answer below, but still using types. It is possible to have multiple occurrences of the same with different types, if they are in a minOccurs/maxOccurs list of a supertype, which is extended by the other types. That is, just like a list of polymorphic classes in java or C#. This overcomes the problem above because although that element name can appear many times in the xml, it only appears once in the xsd.
Here is example xsd and xml - tested with xerces this time!:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="productinfo">
<xs:complexType>
<xs:sequence>
<xs:element name="informationset" type="supertype" minOccurs="2" maxOccurs="2"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="supertype">
</xs:complexType>
<xs:complexType name="Manufacturer">
<xs:complexContent>
<xs:extension base="supertype">
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Ingredients">
<xs:complexContent>
<xs:extension base="supertype">
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
<productinfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<informationset xsi:type="Manufacturer"></informationset>
<informationset xsi:type="Ingredients"></informationset>
</productinfo>
NOTE: You can't control the order of the different types, or how many times each type occurs (each could appear once, many times, or not appear at all) - just as with a list of polymorphic classes in java or C#. But you can at least specify at exact length of the list overall (if you like).
For example, I've restricted the above example to exactly two elements, but the order is not set (i.e. Manufacturer could be first, or Ingredients could be first); and number of repetitions is not set (i.e. they could both be Manufacturer, or both Ingredients, or one of each).
You can, with XML Schema type, as in:
<productinfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<informationset xsi:type="Manufacturer"></informationset>
<informationset xsi:type="Ingredients"></informationset>
</productinfo>
And the XSD defines separate complex types for each one:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="productinfo">
<xs:complexType>
<xs:sequence>
<xs:element name="informationset" type="Manufacturer"/>
<xs:element name="informationset" type="Ingredients"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Manufacturer">
</xs:complexType>
<xs:complexType name="Ingredients">
</xs:complexType>
</xs:schema>
This is a special case for xsi:type
. In general, don't think you can specify attributes to have different values in elements of the same name, because they are different definitions of the same element.
I'm not 100% clear on the precise reason - anyone know the relevant part of the spec?
You could try something like this - create a separate complexType for your "informationSet" elements, and limit the attribute to a list of valid strings:
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="productinfo">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded"
name="informationset" type="informationSetType" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="informationSetType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="type" type="validAttributeType" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="validAttributeType">
<xs:restriction base="xs:string">
<xs:enumeration value="Manufacturer" />
<xs:enumeration value="Ingredients" />
</xs:restriction>
</xs:simpleType>
</xs:schema>
Of course, you can extend that list of valid attribute names if you wish - just add more elements to the restriction-enumeration list there.
Marc
Thanks to XML Schema 1.1, there is a way to enforce your requirement without having to import the xsi
namespace and hack polymorphism into your XML document. XML Schema 1.1 brought two new components, assertions and type alternatives, providing the xs:assert
and xs:alternative
elements. They have a @test
attribute in which conditions and constraints are being specified as XPath 2.0 expressions.
Even though xs:alternative
is obviously meant to be the solution to your problem, I for my part have not yet succeeded in assigning alternative types based on an element's position. (The schema parser's understanding of node contexts seems to differ from the one I would expect based on the validated XML document structure.)
Anyway, you can enforce your constraints using xs:assert
:
<xs:element name="productinfo">
<xs:complexType>
<xs:sequence minOccurs="2" maxOccurs="unbounded">
<xs:element name="informationset">
<xs:complexType>
<xs:attribute name="type" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Manufacturer" />
<xs:enumeration value="Ingredients" />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:assert test="informationset[1]/@type='Manufacturer'" xpathDefaultNamespace="##targetNamespace"/>
<xs:assert test="informationset[2]/@type='Ingredients'" xpathDefaultNamespace="##targetNamespace"/>
</xs:complexType>
</xs:element>
The use of attribute @xpathDefaultNamespace
may not be necessary if your schema's namespace is null, but must be set to ##targetNamespace
if you schema defines one, otherwise evaluation of the element name will fail.
Note: Obviously, for this to work it is necessary to use a validator which supports XML Schema 1.1. When using, say, OxygenXML, you can just set the default XML schema version to 1.1 in the XML / XML Parser / XML Schema preference page. When using Xerces as a validator, an additional feature must be activated:
http://apache.org/xml/features/validation/cta-full-xpath-checking
. Otherwise it will fail to evaluate most of the XPath you come up with because it uses an insufficient subset by default.
May be that http://xsd.stylusstudio.com/2001Aug/post03013.htm is what you are looking for.