My requirement is like:
I need to validate an xml which has an element "Term" and its attributes as termType, ValueName, Value.
I need to add a restriction to termType such that, when I get the termType attribute value as "expression", the ValueName should only be IF or ELSE or THEN, and if the valueName value is "IF" the value should be "1" only.
How can I achieve this?
The xml looks like:
<Term termType="Input" value="97397D89-C044-47CC-8153-267CBBE71D29" valueName="PolicyType"/>
**<Term termType="expression" value="1" valueName="IF"/>**
<Term termType="mask" value="" valueName=""/>
Corresponding XSD I tried is:
<xs:simpleType name="termType">
<xs:restriction base="xs:string">
<xs:enumeration value="expression" />...
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TermType">
<xs:attribute name="termType" use="required" type="termType" />
<xs:attribute name="value" use="required" type="xs:string" />
<xs:attribute name="valueName" use="required" type="xs:string" />
</xs:complexType>
This kind of conditional restriction is a fairly common requirement but is not supported by XSD.
Related
I'm looking for a way to realize a scheme, where an element does have an attribute "topic" only when its enumeration has a specific value like in the upcoming example:
<article>
<type>Blogpost</type>
</article>
<article>
<type topic='news'>Article</type>
</article>
<article>
<type>Comment</type>
</article>
The challenge is to make it that the attribute will only be used when the value "Article" or "Documentary" is chosen. Furthermore I want only enumerations from predefined list to be valid. In case the right enumeration is chosen I want the attribute to be required in all other cases I don't want to have the attribute at all.
So far I tried the following schemes in order to do so but it doesn't work at all:
<xs:element name="type" minOccurs="0">
<xs:alternative test="#name='type'" type="type1"/>
<xs:alternative test="#name='type'" type="type2"/>
<xs:alternative type="xs:error"/>
</xs:element>
(...)
<xs:simpleType name="type1">
<xs:restriction base="xs:string">
<xs:enumeration value="Blogpost"/>
<xs:enumeration value="Comment"/>
<xs:enumeration value="Sentence"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="type2">
<xs:simpleContent>
<xs:extension base="type1">
<xs:attribute name="topic" type="topics" use='required'>
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Article"/>
<xs:enumeration value="Documentary"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:extension>
</xs:simpleContent>
</xs:complexType-->
I always get an error that any chosen element is 'not a valid value of "error"'. The other example I tried worked to make use the attribute however it allowed any text and didn't care about the enumeration restrictions making it basicly useless:
<xs:element name="type" minOccurs="0">
<xs:alternative test="#ItemType='type'" type="type1"/>
<xs:alternative test="#ItemType='type'" type="type2"/>
</xs:element>
(...)
How do I do it that the same element does only have the attribute when it is actually the right enumeration and still the restrictions to enumerations are valid? Is my approach to work with xs:alternative the right one in order to have a same element one time with and one time without an attribute?
I was not able to figure out right test conditions in order to work with xs:assert.
I now have a useful answer - sorry for the non-useful comments!
This question is almost identical yours: XSD 1.1 alternative test the contents of text()
. On that basis, I suggest that you try something like this (not tested, because I don't have a XSD1.1 processor)
<xs:element name="type" type="type2" minOccurs="0"/>
<xs:complexType name="type2">
<xs:simpleContent>
<xs:extension base="type1">
<xs:attribute name="topic" type="topics" use='required'/>
<xs:assert test="not ( #topic or ( #topic and ($value eq 'Article' or $value eq 'Documentary'))"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="type1">
<xs:restriction base="xs:string">
<xs:enumeration value="Article"/>
<xs:enumeration value="Documentary"/>
<xs:enumeration value="Blogpost"/>
<xs:enumeration value="Comment"/>
<xs:enumeration value="Sentence"/>
</xs:restriction>
</xs:simpleType>```
Thank you again for your approach #kimbert.
With your help I was able to figure out the assertion that is fullfilling the demand. I just had to adjust your idea slightly:
<xs:element name="type" type="type2" minOccurs="0"/>
<xs:complexType name="type2">
<xs:simpleContent>
<xs:extension base="type1">
<xs:attribute name="topic" type="topics"/>
<xs:assert test="#topic and ($value eq 'Article' or $value eq 'Documentary') or not ($value eq 'Article' or $value eq 'Documentary')"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="type1">
<xs:restriction base="xs:string">
<xs:enumeration value="Article"/>
<xs:enumeration value="Documentary"/>
<xs:enumeration value="Blogpost"/>
<xs:enumeration value="Comment"/>
<xs:enumeration value="Sentence"/>
</xs:restriction>
</xs:simpleType>
Like this the compiler now always demands the attribute "topic" in case the value "Article" or "Documentary" is asked for. In all other cases the attribute is ignored as it should be.
My new XML private language includes elements <figure>, representing illustrations (image + caption).
Whenever illustrations refer to some image in the local database I just want to type
<figure id="9809" width="full" />
to identify image number 9809 and its associated caption.
On the other side, if images come from outside I will need a slightly different syntax:
<figure href="https://some-url-here" width="full">Some ad hoc catpion</figure>
So far I have declared an element that combine both behaviours, like this:
<!-- Figures -->
<xs:simpleType name="FigureWidthEnum">
<xs:restriction base="xs:token">
<xs:enumeration value="center" />
<xs:enumeration value="half" />
<xs:enumeration value="full" />
</xs:restriction>
</xs:simpleType>
<xs:element name="figure">
<xs:complexType mixed="true">
<xs:complexContent>
<xs:extension base="Inline">
<xs:attribute name="href" type="URI" />
<xs:attribute name="id" type="xs:nonNegativeInteger" />
<xs:attribute name="width" type="FigureWidthEnum" default="full" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
It works fine but a fresh editor can mess with the 3 attributes and type impossible things I don't want to pass the Schema Validator that easy. For example:
<figure id="9809" width="full" href="https://some-unexpected-url">Some unexpected caption that should not be here</figure>
I want to have two completely separate sintaxes for <figure>, as if I could declare these two elements with the same name:
<xs:element name="figure">
<xs:complexType mixed="true">
<xs:complexContent>
<xs:extension base="Inline">
<xs:attribute name="href" type="URI" />
<xs:attribute name="width" type="FigureWidthEnum" default="full" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="figure">
<xs:complexType>
<xs:attribute name="id" type="xs:nonNegativeInteger" />
<xs:attribute name="width" type="FigureWidthEnum" default="full" />
</xs:complexType>
</xs:element>
In fact it is not possible.
Can it be done somehow?
Yes, it is possible with XSD 1.1, which provides <xs:alternative> element intended specifically for such requirements. Here's a complete XML schema I've designed to validate exactly as you need:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
vc:minVersion="1.1">
<!-- Figures -->
<xs:simpleType name="FigureWidthEnum">
<xs:restriction base="xs:token">
<xs:enumeration value="center" />
<xs:enumeration value="half" />
<xs:enumeration value="full" />
</xs:restriction>
</xs:simpleType>
<!-- stub definition -->
<xs:complexType name="Inline"/>
<!-- 'figure' element is declared once, but its type has two alternatives -->
<xs:element name="figure">
<!-- The first alternative is selected, when the condition specified
in 'test' attribute is true. The condition must be a boolean XPath
expression. The '#id' returns the value of 'id' attribute. However,
when converted to boolean, it indicates whether 'id' attribute is
specified at all. The anonymous complexType inside <xs:alternative>
provides the element type valid for this case.
-->
<xs:alternative test="#id">
<xs:complexType>
<xs:attribute name="id" type="xs:nonNegativeInteger" />
<xs:attribute name="width" type="FigureWidthEnum" default="full" />
</xs:complexType>
</xs:alternative>
<!-- The second alternative has no 'test' attribute. That means, it must
be selected by default, when all other alternatives (with a specified
test condition) do not pass. Here, the anonymous complexType inside
<xs:alternative> defines the element type in case of reference:
when 'href' is present.
-->
<xs:alternative>
<xs:complexType mixed="true">
<xs:complexContent>
<xs:extension base="Inline">
<xs:attribute name="href" type="xs:anyURI" />
<xs:attribute name="width" type="FigureWidthEnum" default="full" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:alternative>
</xs:element>
<!-- this element is defined just to test the whole schema in XML below -->
<xs:element name="figures">
<xs:complexType>
<xs:sequence>
<xs:element ref="figure" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
A complete XML file this schema validates:
<?xml version="1.0" encoding="UTF-8"?>
<figures>
<!-- passes validation -->
<figure id="9809" width="full" />
<!-- passes validation -->
<figure width="full" href="https://some-unexpected-url">Some ad hoc caption</figure>
<!-- does not pass the validation -->
<figure id="9809" width="full" href="https://some-unexpected-url">
Some unexpected caption that should not be here
</figure>
</figures>
The validation was done with Apache Xerces 2.11.0 (XSD 1.1 aware).
Promo add-on. These links may be interesting for those working with XML schemas and WSDL: FlexDoc/XML XSDDoc & WSDLDoc – High-performance universal XML Schema / WSDL Documentation Generators with Diagrams
I want to write an XML schema (xsd 1.1) for a document containing options. Each option has a name and a type (like boolean, integer, string, etc ...) and a datum matching that type. The list of types is fixed, but quiet long. (Only 3 are listed in listing 3 for simplicity.)
How do I do this without a ridiculous amount of repetition?
Use case 1
Here is a valid document for this schema..
Listing 1:
<abc:options>
<abc:option name="is-enabled" type="boolean">false</abc:option>
<abc:option name="wing-span" type="float">1.2</abc:option>
</abc:options>
Use case 2
This document is not valid for this schema because the simple type bit is wrong for the #type attribute.
<abc:options>
<abc:option name="is-enabled" type="boolean">24</abc:option>
<abc:option name="wing-span" type="float">this-is-not-a-number!</abc:option>
</abc:options>
What I have tried so far ...
Listing 3 is my attempt so far. But it is bad because I have to re-declare the #name attribute for each datum type. Is there a better solution? That is to say, one where I don't have to re-declare the #name attribute for each possible datum type.
Listing 3:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:abc="http://www.example.com"
targetNamespace="http://www.example.com"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="options">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:element name="abc:option" type="option-Type"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:simpleType name="option-Datum-Type">
<xs:restriction base="xs:string">
<xs:enumeration value="boolean"/>
<xs:enumeration value="integer"/>
<xs:enumeration value="float"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="option-Type-boolean">
<xs:simpleContent>
<xs:extension base="xs:boolean">
<xs:attribute name="name" type="xs:token" use="required" />
<xs:attribute name="type" type="abc:option-Datum-Type" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="option-Type-string">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" type="xs:token" use="required" />
<xs:attribute name="type" type="abc:option-Datum-Type" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="option-Type-float">
<xs:simpleContent>
<xs:extension base="xs:double">
<xs:attribute name="name" type="xs:token" use="required" />
<xs:attribute name="type" type="abc:option-Datum-Type" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="option-Type">
<xs:alternative test="#type='boolean'" type="abc:option-Type-boolean"/>
<xs:alternative test="#type='string'" type="abc:option-Type-string" />
<xs:alternative test="#type='float'" type="abc:option-Type-float" />
<xs:alternative type="xs:error"/>
</xs:complexType>
</xs:schema>
If the type can only be one of the atomic types you can use xs:assert like this:
<xs:complexType name="option-Type">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" type="xs:token" use="required" />
<xs:attribute name="type" type="xs:string" use="required" />
<xs:assert
test="if (#type='boolean') then . castable as xs:boolean
else if (#type='float') then . castable as xs:float
else if (#type='int') then . castable as xs:int
else if (#type='string') then . castable as xs:string
else false()"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
Notes:
You don't need to declare any new type. If you want you can even also skip the declaration of the enumeration.
Using this approach you need a new line for every new possible type (you don't really need a new line but it is easy to read with every type in a different line).
You can use text() instead of . if you found it to be more clear
Notice how simply this approach would be if XPath 2.0 had an eval function similar to javascript and other languages eval function:
<xs:assert test="eval(text() || ' castable as xs:' || #type)"/>
I though an eval/parse function was going to be added to XPath 3.0 spec but I think it finally has not been added.
Unlike instance of you cannot use lists (*,+) other than ? with castable as operator. You can only use atomic types using this approach.
Cast to string string should always succed as the type is declared as xs:string.
I'm new in XSD and have the following question, thanks a lot for your help!
Suppose I have the xml:
<DataGroup>
<Data name="name">Jane</Data>
<Data name="age">50</Data>
<Data name="state>MA</Data>
<Data name="zipcode">01000</Data>
</DataGroup>
I'd like to put restriction on:
when the attribute name equals to "age", the value of should be an integer and >20,
when the attribute name equals to "state", the value of should be two letters.
when the attribute name equals to "zip code", the value of should be \d{5}.
Can not modify the xml, any idea?
Thanks!
Using XSD 1.1 you can declare alternative types for each situation. Additionally you can restrict the number of <Data> elements to exactly four, and add an assertion to guarantee that each different attribute occurs exactly once:
<xs:element name="DataGroup">
<xs:complexType>
<xs:sequence>
<xs:element name="Data" maxOccurs="4" minOccurs="4">
<xs:alternative type="NameData" test="#name='name'" />
<xs:alternative type="AgeData" test="#name='age'" />
<xs:alternative type="ZipData" test="#name='zipcode'"/>
<xs:alternative type="StateData" test="#name='state'"/>
</xs:element>
</xs:sequence>
<xs:assert test="Data/#name='name' and Data/#name='age' and Data/#name='zipcode' and Data/#name='state'"></xs:assert>
</xs:complexType>
</xs:element>
Since <Data> is a simple type, you need to declare the attribute as an extension of each type:
<xs:complexType name="NameData">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="AgeData">
<xs:simpleContent>
<xs:extension base="AgeType">
<xs:attribute name="name" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="ZipData">
<xs:simpleContent>
<xs:extension base="ZipType">
<xs:attribute name="name" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="StateData">
<xs:simpleContent>
<xs:extension base="StateType">
<xs:attribute name="name" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
The restrictions you declare in the types which are base for the simple types:
<xs:simpleType name="AgeType">
<xs:restriction base="xs:integer">
<xs:minExclusive value="20"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="StateType">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{2}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ZipType">
<xs:restriction base="xs:string">
<xs:pattern value="\d{5}"/>
</xs:restriction>
</xs:simpleType>
This will validate your file with the restrictions you require.
I have the following xml:
<animals>
<animal name="Pongo" animalType="Dog" />
<animal name="Marie" animalType="Cat" />
<animal name="Skippy" animalType="Kangaroo" />
</animals>
I know it is possible to restrict the type of animals using an enum like this:
<xs:simpleType name="animalType">
<xs:restriction base="xs:string">
<xs:enumeration value="Cat" />
<xs:enumeration value="Dog" />
<xs:enumeration value="Kangaroo" />
</xs:restriction>
</xs:simpleType>
What I whould like is to know automatically, based on the animalType value, how many shoes does the animal need, after the xml parsing.
And, when a new animal type is being added, to add also the number of walking legs for that animal.
For instance, it would be great to be possible to define something like
<xs:simpleType name="animalType">
<xs:restriction base="xs:string">
<xs:enumeration value="Cat" nbOfShoes="4" />
<xs:enumeration value="Dog" nbOfShoes="4" />
<xs:enumeration value="Kangaroo" nbOfShoes="2" />
</xs:restriction>
</xs:simpleType>
Is it possible to achieve this using xsd, or I have to implement the numberOfShoes logic after the xml is being parsed?
Thank you.
It depends. In XSD you can define xml from structure point of view. The relation between structure and content is more difficult to express in XSD 1.0.
You could use substitution of types using xsi:type attribute. The XSD could look like
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<!-- Let be Animals root element -->
<xs:element name="Animals" type="animals" />
<!-- type for Animals elemes -->
<xs:complexType name="animals">
<xs:sequence>
<xs:element name="Animal" maxOccurs="unbounded" type="animal"/>
</xs:sequence>
</xs:complexType>
<!-- Define an abstract type for animal (abstract = there shouldn't occure element of this type only of its childs). It has no attributes. -->
<xs:complexType name="animal" abstract="true">
<xs:simpleContent>
<xs:extension base="xs:string"/>
</xs:simpleContent>
</xs:complexType>
<!-- Define a type for cat... -->
<xs:complexType name="catType">
<xs:simpleContent>
<!-- ... it extends abstract animal type ... -->
<xs:extension base="animal">
<!-- ... and add some attributes with fixed values -->
<xs:attribute name="name" fixed="cat" />
<xs:attribute name="nbOfLegs" fixed="4" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- similar definition like catType -->
<xs:complexType name="kangarooType">
<xs:simpleContent>
<xs:extension base="animal">
<xs:attribute name="name" fixed="kangaroo" />
<xs:attribute name="nbOfLegs" fixed="2" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
Following XML should validate
<?xml version="1.0" encoding="UTF-8"?>
<Animals xsi:noNamespaceSchemaLocation="animals.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- xsi:type is saying what type is actually used -->
<Animal xsi:type="catType" name="cat" nbOfLegs="4" />
<Animal xsi:type="kangarooType" name="kangaroo" nbOfLegs="2" />
</Animals>
Following not
<?xml version="1.0" encoding="UTF-8"?>
<Animals xsi:noNamespaceSchemaLocation="animals.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Animal xsi:type="catType" name="cat" nbOfLegs="2" />
</Animals>
(with error like Value '2' of attribute 'nbOfLegs' is not equal to the fixed default value '4').