xml schema: restricting occurrences to sibling element sequence cardinality - xsd

Given:
<xs:complexType name="SymbolsList" final="">
<xs:sequence>
<xs:element name="symbol" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ComboList">
<xs:sequence>
<xs:element name="combo" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="symbol" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="comboName" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:element name="symbolsList" type="SymbolsList">
<xs:unique name="uniqueSymbol">
<xs:selector xpath="./symbol" />
<xs:field xpath="#name" />
</xs:unique>
</xs:element>
<xs:element name="combosList" type="ComboList">
<xs:unique name="uniqueCombo">
<xs:selector xpath="./combo" />
<xs:field xpath="#comboName" />
</xs:unique>
</xs:element>
I believe this defines a list of symbols and a list of combinations of those symbols.
The each entry in the list of symbols must have a unique name, and each entry in the list of combos must have a unique comboName.
What I'd like to know is if there is a way for me to restrict the number of allowed occurrences in the combosList sequence to at least the number of symbols defined in the symbol list.
I guess I'm asking whether or not cardinality restriction can be variable and if so, how to associate it's limitation?
I also want to make it so that the comboList elements (a single combo) can only use names of symbols defined in the symbolList element.
I think I can pull of that last part. I can't find anything anywhere that talks about limiting caridinal sizes of disparate element sequences to greater than or equal to one or the other.
Perhaps it's not possible.

XSD requires cardinality to constraints to be specified literally in the declaration; the kind of dynamic calculation you have in mind is not in XSD's design space.
In XSD 1.1 you can add an assertion to some common ancestor of SymbolsList and CombosList that requires
count(CombosList/combo) ge count(SymbolsList/symbol)
XSD 1.1 is supported by Saxon EE and by Xerces J (in the latter case you have to look for the 1.1 distribution, or did last I looked). (One caveat: Note that Xerces J does not support all of XPath 2.0 in assertions, and I haven't actually checked to see whether this assertion is covered by the minimal subset of XPath XSD requires of conforming 1.1 implementations. Investigate further before sinking a lot of time here.)

Related

Unique constraint on a complexType instead of an element

I have the following XSD structure:
<xs:schema xmlns:ns="http://abc/">
...
<xs:element name="abc">
<xs:complexType>
<xs:sequence>
<xs:element ref="map"/>
</xs:sequence>
</xs:complexType>
</xs:element>
...
<xs:element name="map">
<xs:complexType>
<xs:sequence>
<xs:element name="entry" type="ns:MapEntryType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:unique name="entry">
<xs:selector xpath="entry"/>
<xs:field xpath="key"/>
</xs:unique>
</xs:element>
<xs:complexType name="MapEntryType">
<xs:sequence>
<xs:element name="key" type="xs:string"/>
<xs:element name="value" type="xs:anyType"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
This is doing its job.
The map element now has to be called something different based on whichever is the wrapper, so the name is sometimes map, sometimes properties, sometimes options, etc.
Therefore I want to genericize the map element.
I tried doing the following:
Making map a xs:complexType and changing ref to type.
This resulted in xs:unique not being accepted and failed
Making map a xs:complexType, changing ref to type and moving the xs:unique constraint to the element definitions.
This worked but resulted in the XSD having a lot of xs:unique present in the document.
Isn't there a way to simply tell that I want a specific structure and it containing unique elements without having to repeat the unique constraint everywhere?
As Petru Gardea said in his answer
Both XSD 1.0 and 1.1 place the identity constraints under an element
So you have to add xs:unique to every element, but if you are using XSD 1.1 you can define only once a complete xs:unique and then in the rest of the elements use xs:unique ref="name". This is not valid for you as you are using XSD 1.0, but I let it here for future XSD 1.1 users that find this good question.
Example (namespaces removed for clarity):
<xs:element name="map">
<xs:complexType>
<xs:sequence>
<xs:element name="entry" type="MapEntryType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- Only completely defined once -->
<xs:unique name="uniqueEntry">
<xs:selector xpath="entry"/>
<xs:field xpath="key"/>
</xs:unique>
</xs:element>
<xs:element name="hashMap">
<xs:complexType>
<xs:sequence>
<xs:element name="entry" type="MapEntryType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- Referenced here and every other time -->
<xs:unique ref="uniqueEntry"/>
</xs:element>
Short answer, it is not possible. Both XSD 1.0 and 1.1 place the identity constraints under an element; a constraint cannot be globally defined, therefore there is no "reuse" per se, other than that of the enclosing element. Given your scenario (different element names for different needs) it is not possible to reuse.

defining different sets of child nodes by attribute value

I'm trying to define a schema for some xml-based database exchange like this:
<table name="foo">
<row>
<fooid>15</fooid>
<fooname>some entry</fooname>
</row>
<row>
<fooid>28</fooid>
<fooname>something else</fooname>
</row>
</table>
<table name="bar">
<row>
<barid>19</barid>
<barcounter>93</barcounter>
</row>
</table>
so I have several of these tables and within these tables there should be only the fields that exist in these tables. For example barid should not appear in table foo.
Is there any way to define this?
Yes, there are two ways. One is simple (and relies on some human intuition and documentation), and the other is more expressive (but inevitably also a bit more complicated.)
The simple way is to replace the names 'table' and 'row' with names that indicate what table we are talking about:
<table-foo>
<row-foo>
<fooid>28</fooid>
<fooname>something</fooname>
</row-foo>
...
</table-foo>
<table-bar>
<row-bar>
<barid>19</barid>
<barcounter>93</barcounter>
</row-bar>
...
</table-bar>
XSD validation (like validation using DTDs and Relax NG) is based principally on the element names used. If you want two different kinds of row to contain different things, give them two different names. So foo-table and its descendants can be declared thus:
<xs:element name="table-foo" substitutionGroup="tns:table">
<xs:complexType>
<xs:sequence>
<xs:element ref="tns:row-foo"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="row-foo" substitutionGroup="tns:row">
<xs:complexType>
<xs:sequence>
<xs:element ref="tns:fooid"/>
<xs:element ref="tns:fooname"/>
</xs:sequence>
</xs:complexType>
And similarly for bar-table and bar-row.
Sometimes, however, we absolutely must, or really want to, capture the fact that both 'row-foo' and 'row-bar' have something crucial in common. They are both 'rows' in some abstract ontology, and that may matter to us. In such cases, you can use abstract elements to capture the regularity.
For example, here is a simple abstraction for tables, rows, and cells:
<xs:element name="table"
abstract="true"
type="tns:table"/>
<xs:element name="row"
abstract="true"
type="tns:row"/>
<xs:element name="cell"
abstract="true"
type="xs:anySimpleType"/>
The types for table and row are straightforward:
<xs:complexType name="table">
<xs:sequence>
<xs:element ref="tns:row" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="row">
<xs:sequence>
<xs:element ref="tns:cell" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
Now, the declarations for table-foo etc. become slightly more complicated, because for each declaration we have to establish a relation to the abstraction we have just defined. Element foo-table is an instantiation of the table abstraction, and its type is a restriction of the abstract table type:
<xs:element name="table-foo"
substitutionGroup="tns:table">
<xs:complexType>
<xs:complexContent>
<xs:restriction base="tns:table">
<xs:sequence>
<xs:element ref="tns:row-foo"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:element>
Element foo-row is similar: we specify that it's a "row" by using the substitutionGroup attribute, and we derive its complex type by restriction from the abstract row type:
<xs:element name="row-foo" substitutionGroup="tns:row">
<xs:complexType>
<xs:complexContent>
<xs:restriction base="tns:row">
<xs:sequence>
<xs:element ref="tns:fooid"/>
<xs:element ref="tns:fooname"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:element>
Note that we don't allow arbitrary cells to appear here, just the two cell types we want for rows from table foo. And to close off the pattern, we declare that the elements fooid and fooname are cells, using (again) substitutionGroup.
<xs:element name="fooid" type="xs:integer"
substitutionGroup="tns:cell"/>
<xs:element name="fooname" type="xs:string"
substitutionGroup="tns:cell"/>
The same patterns can be used to declare a different set of legal cells for table bar:
<xs:element name="barid" type="xs:positiveInteger"
substitutionGroup="tns:cell"/>
<xs:element name="barcounter" type="xs:double"
substitutionGroup="tns:cell"/>
<xs:element name="table-bar" substitutionGroup="tns:table">
<xs:complexType>
<xs:complexContent>
<xs:restriction base="tns:table">
<xs:sequence>
<xs:element ref="tns:row-bar"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="row-bar" substitutionGroup="tns:row">
<xs:complexType>
<xs:complexContent>
<xs:restriction base="tns:row">
<xs:sequence>
<xs:element ref="tns:barid"/>
<xs:element ref="tns:barcounter"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:element>
The situation you describe is one of the use cases for which abstract elements and substitution groups were designed. Other techniques which could also be used here (but which I won't illustrate in detail) include:
Declared subtypes, use of xsi:type (declare foo-table and bar-table as restrictions or extensions of type table, use <table xsi:type="tns:foo-table">...</table> or <table xsi:type="tns:bar-table">...</table> to guide validation)
Assertions (declare foo-table and bar-table types which extend the generic table type by adding assertions about the grandchildren -- this is an XSD 1.1 feature not available in 1.0).
Conditional type assignment (declare that table gets one type if it has name="foo" and a different type if it has name="bar" -- also an XSD 1.1 feature not available in 1.0).
There may be other ways to do it, too.

Difference of mixed="true" and xs:extension in XML Schema

What is the practical diference between these two:
<xs:element name="A">
<xs:complexType mixed="true">
<xs:attribute name="att" type="xs:boolean"/>
</xs:complexType>
</xs:element>
<xs:element name="B">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="att" type="xs:boolean"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
The two are different. Your first example uses mixed="true" which denotes mixed content, i.e. character data mixed in with child elements. Whereas your second example restricts the element content to the xs:string type. Both indicate the presence of an attribute.
With your example, both are practically the same. However, if you do not plan on having mixed content, i.e. you do not plan to add child elements, the second version is much clearer.

xsd: How to extend a type with an unordered list of elements

This is a part of my xml schema
<xs:complexType name="Friend">
<xs:all>
<xs:element name="name" type="xs:string" />
<xs:element name="phone" type="xs:string" />
<xs:element name="address" type="xs:string" />
</xs:all>
</xs:complexType>
<xs:complexType name="Coworker">
<xs:all>
<xs:element name="name" type="xs:string" />
<xs:element name="phone" type="xs:string" />
<xs:element name="office" type="xs:string" />
</xs:all>
</xs:complexType>
For better maintainability, I would like to have the shared attributes in an (abstract) super type or something like that. But more important, I want that all elements are unordered and also optional.
Is this possible, and what is the best way to do it?
You have to limit yourself a little bit, some of the things you are trying to do are not possible in XML Schema.
Suppose you introduce a complex type called Person to be a super-type of Friend and Coworker. Here are your options:
Replace xs:all with xs:sequence, remove name and phone from the sub-types, add to the super-type, and add inheritance. Your elements now have to be ordered, but you can make them individually optional. It is illegal to use xs:all in type hierarchies in XML Schema, because the processor cannot tell where the parent content model stops and the child content model starts.
Replace xs:all with <xs:choice maxOccurs="unbounded"> in both types, and add your inheritance. Then your elements become unordered again, but they may repeat.
So in conclusion: given your type names up there, I would guess that your requirements will not be exactly met. I would go for the first option: insisting on arbitrary element order is often not as useful as it seems.
One-and-half year after this question and the accepted answer were posted, XSD 1.1 was published. In this version it is possible to specify what the OP asked for because a number of restriction on xs:all were lifted. One of them is that it is now possible to extend an xs:all.
Using XSD 1.1 you can specify the following:
<xs:complexType name="Person" abstract="true">
<xs:all>
<xs:element name="name" type="xs:string" minOccurs="0" />
<xs:element name="phone" type="xs:string" minOccurs="0" />
</xs:all>
</xs:complexType>
<xs:complexType name="Friend">
<xs:complexContent>
<xs:extension base="Person">
<xs:all>
<xs:element name="address" type="xs:string" minOccurs="0" />
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Coworker">
<xs:complexContent>
<xs:extension base="Person">
<xs:all>
<xs:element name="office" type="xs:string" minOccurs="0" />
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
This defines the following types:
Person: an abstract type with optional unordered name and phone elements;
Friend: extends Person adding an optional address element to the list of unordered elements;
Coworker: extends Coworker adding an optional office element to the list of unordered elements.
Note that this solution does not work for every XML processor: even though 8 years have passed since the publication of XSD 1.1, a lot of processors still only support XSD 1.0.

Schema Issue: Can define element type OR add element attribute, but not both. I want both!

I've inherited the task of creating a schema for some XML which already exists - and IMHO is not the best that could have been done. The section giving me problems is the element at the end of the 'scan-result' element.
The best I'm hoping for with regard to the data in the 'spectrum' element is to treat it as type="xs:string". I'll programatically divide up the numeric pairs that constitute the data in the string later. (Even though this step would not be needed had the data been properly structured in the first place.)
Here's a similar piece of XML data to what I have to work with...
<scan-result>
<spectrum-index>0</spectrum-index>
<scan-index>2</scan-index>
<time-stamp>5609</time-stamp>
<tic>55510</tic>
<start-mass>22.0</start-mass>
<stop-mass>71.0</stop-mass>
<spectrum count="5">30,11352;31,360;32,16634;45,1161;46,26003</spectrum>
</scan-result>
The problem is, I can't seem to get a working definition for the 'spectrum' element that has the 'count' attribute and allows me to define the 'spectrum' element type as "xs:string".
What I would like is something like the following:
<xs:complexType name="ctypScanResult">
<xs:sequence>
<xs:element name="spectrum-index" type="xs:integer"/>
<xs:element name="scan-index" type="xs:integer"/>
<xs:element name="time-stamp" type="xs:integer"/>
<xs:element name="tic" type="xs:integer"/>
<xs:element name="start-mass" type="xs:float"/>
<xs:element name="stop-mass" type="xs:float"/>
<xs:element name="spectrum" type="xs:string">
<xs:complexType>
<xs:attribute name="count" type="xs:integer"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="count" type="xs:integer"/>
</xs:complexType>
The problem is that I can define the type of the 'spectrum' element as "xs:string" XOR I can define the anonymous 'xs:complexType' in the 'spectrum' element, which allows me to insert the 'count' attribute. But I need to be able to express both.
Given that I'm kind of stuck with the XML as it was handed to me, is there a schema definition that will allow me to describe this data?
Sorry this is long, but thanks to any and all who respond,
AlarmTripper
Followup: I know why the error occurs...
Quoted from W3C:
3.3.3 Constraints on XML Representations of Element Declarations
Schema Representation Constraint: Element Declaration Representation OK
In addition to the conditions imposed on element information items by the schema for schemas: all of the following must be true:
1 default and fixed must not both be present.
2 If the item's parent is not , then all of the following must be true:
2.1 One of ref or name must be present, but not both.
2.2 If ref is present, then all of , , , , , nillable, default, fixed, form, block and type must be absent, i.e. only minOccurs, maxOccurs, id are allowed in addition to ref, along with .
3 type and either or are mutually exclusive.
4 The corresponding particle and/or element declarations must satisfy the conditions set out in Constraints on Element Declaration Schema Components (§3.3.6) and Constraints on Particle Schema Components (§3.9.6).
But I'm still in the same fix I was before... How can I actually accomplish something that resembles my goal?
Thanks,
AlarmTripper
Let a tool do it for you! Try xsd.exe.
Or, if you must define by hand, at least check your hand-written-definition with an automatically generated one.
Here's what XSD.exe gave me for your input. I trimmed out some MS-NS cruft.
<xs:element name="spectrum">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="count" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
You need to set the attribute mixed="true" on complexType:
<xs:element name="spectrum">
<xs:complexType mixed="true">
<xs:attribute name="count" type="xs:integer" />
</xs:complexType>
</xs:element>
EDIT: Okay, just read your comment, sorry. I believe the following should work instead:
<xs:element name="spectrum">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="count" type="xs:integer" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="spectrum" type="xs:string">
<xs:complexType>
<!-- ADD THIS NEXT LINE -->
<xs:complexContent mixed="true"/>
<xs:attribute name="count" type="xs:integer"/>
</xs:complexType>
</xs:element>

Resources