Reputation: 355
Climbing up the XML learning curve, I've slipped on a Validation issue with the use of the list
element. I'm attempting to create a list
element that would allow only a specific key
's values as members.
A little context to help you model what follows. We have a variety of SOURCE
s each with unique NAME
. The NAME
format is restricted by a pattern in a simpleType element; REFDESType
. The NAME
uniqueness is enforced by key source_key
.
Now, attempts to make productive use of this source_key
to provide input limits on related data fields are not yet yielding the intended results. POWERSOURCE
is one of these intended usage fields and is the child to element MODE
. The intent is for POWERSOURCE
to be capable of list
ing one or more of the SOURCE NAME
s but fails MSXML parse. {OK, welcome back after picking yourself up off the floor laughing.} Yes, this is being developed for a standalone EXCEL 2013 based app without 3rd party libraries.
One issue that seems to be glaring (to me anyway) is that REFDESType
trees into POWERSOURCE
from two directions. Once thru NAME
and again thru REFDESList
. Hmm...
The Parse Error: The keyref
'GEN-1 GEN-2 GEN-3 GEN-4' does not resolve to a key
for the Identity Constraint '{http://www.myCo.com}source_key'.
Certainly, the utility of using list
is apparent to me. Now, if I could only understand how this way is not working. Alternatives approaches are also welcome.
ELA.XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://www.myCo.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ela="http://www.myCo.com" elementFormDefault="qualified">
<xs:element name="DB">
<xs:complexType>
<xs:sequence>
<xs:element ref="ela:SOURCE" maxOccurs="unbounded"/>
<xs:element ref="ela:MODE" minOccurs="6" maxOccurs="13"/>
</xs:sequence>
</xs:complexType>
<xs:key name="source_key">
<xs:selector xpath="ela:SOURCE"/>
<xs:field xpath="ela:NAME"/>
</xs:key>
</xs:element>
<xs:element name="SOURCE" type="ela:SOURCEType"/>
<xs:element name="NAME" type="ela:REFDESType"/>
<xs:element name="MODE" type="ela:MODEType"/>
<xs:element name="POWERSOURCE" type="ela:REFDESList">
<xs:keyref name="powersource_ref" refer="ela:source_key">
<xs:selector xpath="."/>
<xs:field xpath="."/>
</xs:keyref>
</xs:element>
<xs:simpleType name="REFDESList">
<xs:list itemType="ela:REFDESType"/>
</xs:simpleType>
<xs:simpleType name="REFDESType">
<xs:restriction base="xs:string">
<xs:minLength value="2"/>
<xs:maxLength value="9"/>
<xs:pattern value="[A-Z]([A-Z0-9; -]){1,8}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="MODEType">
<xs:sequence>
<xs:element ref="ela:POWERSOURCE"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="SOURCEType" mixed="true">
<xs:sequence>
<xs:element ref="ela:NAME"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
sample.XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ela:DB xmlns:ela="http://www.myCo.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.myCo.com ELA.xsd">
<ela:SOURCE>
<ela:NAME>GEN-1</ela:NAME>
</ela:SOURCE>
<ela:SOURCE>
<ela:NAME>GEN-2</ela:NAME>
</ela:SOURCE>
<ela:SOURCE>
<ela:NAME>GEN-3</ela:NAME>
</ela:SOURCE>
<ela:SOURCE>
<ela:NAME>GEN-4</ela:NAME>
</ela:SOURCE>
<ela:MODE>
<ela:POWERSOURCE>GEN-1 GEN-2 GEN-3 GEN-4</ela:POWERSOURCE>
</ela:MODE>
</ela:DB>
Upvotes: 1
Views: 181
Reputation: 355
@Michael Kay, @Meyer;
Unless I'm missing something (and it turns out that I was), it turns out that the IDREFS suggestion does NOT work with MSXML after all (unless you dispose of the list element within the simpleType altogether). It works fine in the XMLSpy development tool but once the schema is attempting to be loaded into Excel 2013 via SchemaCollection.Add
method it throws this error.
List datatype must be derived from an atomic datatype or a union of atomic datatypes.
This error message seems contradictory to the 'reported' MSXML LIST element definition for itemType
which is...
The name of a built-in data type or simpleType element defined in this schema (or another schema indicated by the specified namespace). The simpleType element containing the list element is derived from the simple type specified by the list value. The list value must be a qualified name (QName). The use of the simpleType element child and the itemType attribute is mutually exclusive.
I believe my schema meets the LIST requirements. "SOURCENAMEType" is the originating simpleType from which the LIST simpleType "IDREFListType" is derived. ORIGINAL XML: A-Z{1,8}"/>
<xs:simpleType name="IDREFListType">
<xs:list itemType="xs:IDREFS"/>
</xs:simpleType>
<xs:element name="SOURCELIST" type="ela:IDREFListType"/>
REVISED VERSION THAT ACTUALLY WORKS: I'm re-writing this as generic XML so that it can be easily copied and re-used. Notice that myLISTType does NOT contain any xs:list element even though that is exactly how it will function. Reason is due to the plural form of atomic element that populates it, i.e., xs:IDREFS which is already a Collection element. In MSXML they apparently do away with declaring the list element (in this type of case) so that their parser does not interpret it as a list of lists which according to the W3C is not permitted. (My supposition based on extensive testing.) Trouble is, I don't find this little nuance documented anywhere. Augh!!!
<xs:schema targetNamespace="http://www.myCo.com" xmlns:pfx="http://www.myCo.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="IDNAME" type="pfx:myIDNAMEType" maxOccurs="unbounded"/>
<xs:element name="myLIST" type="pfx:myLISTType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:simpleType name="myIDNAMEType">
<xs:restriction base="xs:ID">
<xs:pattern value="[A-Z]([A-Z0-9; -]){1,8}"/>
<!-- pattern for illustrative purposes only, not req'd -->
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="myLISTType">
<xs:restriction base="xs:IDREFS">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
<!-- min/max for illustrative purposes only, not req'd -->
<!-- defines limits for # of members permitted in list -->
</xs:restriction>
</xs:simpleType>
</xs:schema>
XML:
<pfx:IDNAME>BOGUS-1</pfx:IDNAME>
<pfx:IDNAME>BOGUS-2</pfx:IDNAME>
<pfx:IDNAME>BOGUS-3</pfx:IDNAME>
<pfx:IDNAME>BOGUS-4</pfx:IDNAME>
<pfx:myLIST>BOGUS-1 BOGUS-2 BOGUS-3 BOGUS-4</pfx:myLIST>
Validation detects the following errors (based on restriction facets as shown):
Upvotes: 0
Reputation: 1712
It is not possible to match individual list members with keys, only the full list. In your XML file, the value of <ela:POWERSOURCE>
is a list with four items. However, there is no <ela:NAME>
to match that value (i.e. a list with the same four items).
Another problem with your schema is that the xs:keyref
references a xs:key
that is defined in an ancestor, which is not allowed. The referenced key must be defined in the same element declaration, or in a descendant of the element.
A possible solution with key constraints is to use dedicated elements for each list item:
<ela:MODE>
<ela:POWERSOURCE>
<ela:NAME>GEN-1</ela:NAME>
<ela:NAME>GEN-2</ela:NAME>
<ela:NAME>GEN-3</ela:NAME>
<ela:NAME>GEN-4</ela:NAME>
</ela:POWERSOURCE>
</ela:MODE>
In the schema, you could then simply change the REFDESList
type to:
<xs:complexType name="REFDESList">
<xs:sequence>
<xs:element ref="ela:NAME" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
And move the keyref to the same element as the key:
<xs:element name="DB">
<xs:complexType>
<xs:sequence>
<xs:element ref="ela:SOURCE" maxOccurs="unbounded"/>
<xs:element ref="ela:MODE" minOccurs="1" maxOccurs="13"/>
</xs:sequence>
</xs:complexType>
<xs:key name="source_key">
<xs:selector xpath="ela:SOURCE"/>
<xs:field xpath="ela:NAME"/>
</xs:key>
<xs:keyref name="powersource_ref" refer="ela:source_key">
<xs:selector xpath="ela:MODE/ela:POWERSOURCE/ela:NAME"/>
<xs:field xpath="."/>
</xs:keyref>
</xs:element>
As @Michael Kay mentioned, there is a legacy mechanism
using the xs:ID and
xs:IDREFS types.
To apply them to your schema, remove the xs:key
and xs:keyref
elements, and replace the supertype of REFDESType
:
<xs:simpleType name="REFDESType">
<xs:restriction base="xs:ID"> <!-- Restrict legacy ID type -->
<xs:minLength value="2"/>
<xs:maxLength value="9"/>
<xs:pattern value="[A-Z]([A-Z0-9; -]){1,8}"/>
</xs:restriction>
</xs:simpleType>
Now, the value of a <ela:NAME>
element is a subtype of ID, and can be referenced by values of type IDREF or IDREFS:
<xs:element name="POWERSOURCE" type="xs:IDREFS"/> <!-- List of ID references -->
The adjusted XSD should validate your XML file, if the used tool conforms to the standard. Note however that the standard recommends that ID and IDREFS are only used for attributes, i.e.
<ela:NAME id="GEN-1"/>
...
<ela:POWERSOURCE ref="GEN-1 GEN-2 GEN-3 GEN-4"/>
Upvotes: 1
Reputation: 163322
Your keyref says that the value as a whole (that is "GEN-1 GEN-2 GEN-3 GEN-4") must match some key value.
I don't think there's any way with keyref to achieve the same effect as the IDREFS type where each item in the list must satisfy the integrity constraint individually. You would need to use XSD 1.1 assertions for this.
Upvotes: 1