views:

1179

answers:

4

Is it possible to create an XML Schema which imposes a co-occurrence constraint to an attribute/element pair?

<primitive-list>
    <primitive name="P1">
     <definition><!-- primitive specification --></definition>
    </primitive>
    <primitive name="P2">
     <definition><!-- primitive specification --></definition>
    </primitive>

    <!-- other common primitives are specified here-->

<primitive-list>

<composite-list>
    <composite name="C1">
     <primitive ref="P1" />
     <primitive ref="P2" />
     <primitive>
      <definition><!-- inline primitive specification --></definition>
     </primitive>  
    </composite>

    <!-- Other compisites are specified here-->

</composite-list>

The schema should imply that:

  • If a primitive element is specified inside a primitive-list element, then it should contain the name attribute and the embedded definition element, but not the ref attribute.
  • If a primitive element is specified in the composite element, then it should contain either the ref attribute or the definition element. The name is allowed in neither cases.

I am pretty sure that it is possible since the element element in XML Schema itself behaves just like that. So anybody who is in possession of that sacred knowledge please share :-)

Thank you in advance.

A: 

Yes, this is possible. When creating your XML Schema, you will be creating a complex type for each scenario, based on where in the XML tree the element is defined.

If I get a moment later on i can try to actually get an example out here for you, I just don't have the time to get it all formatted perfectly for posting here.

I personally highly recommend looking at this w3schools tutorial, as it should get you what you need.

Mitchel Sellers
Could you please elaborate on how to create a complex type which allows either <b>ref</b> attribute or <b>definition</b> element, but not both of them?
Maxim Vladimirsky
I have to dig up a few references here to get the syntax 100% correct. The key is that you are going to need to create named types to handle this, if I recall correctly. I'll try to post up a full solution yet tonight.
Mitchel Sellers
A: 

Ok, here is a sample, this gets you close, the only thing not handled is the primitive and ref attribute on the composite. Looking at what I can find, it does almost seem to be impossible to do this via a schema. I'm not 100% sure though, but in all cases where I have seen this done, a schema like this is used for high level validation and process code is then used to validate the individual items.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="http://www.iowacomputergurus.com/stackoverflow/samples/xsdexample"
    elementFormDefault="qualified"
    xmlns="http://www.iowacomputergurus.com/stackoverflow/samples/xsdexample"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>

  <xs:complexType name="PrimitiveType">
    <xs:sequence>
      <xs:element name="definition" type="xs:string" minOccurs="1" maxOccurs="1" />
    </xs:sequence>
    <xs:attribute name ="name" use="required" type="xs:string" />
  </xs:complexType>

  <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
          <xs:element name="primitive-list" minOccurs="1" maxOccurs="1">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="primitive" type="PrimitiveType" minOccurs="1" maxOccurs="unbounded" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
          <xs:element name="composite-list" minOccurs="1" maxOccurs="1">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="composite">
                  <xs:complexType>
                    <xs:sequence>
                      <xs:element name="primitive" minOccurs="1" maxOccurs="unbounded">
                        <xs:complexType>
                          <xs:sequence>
                            <xs:element name="definition" minOccurs="0" maxOccurs="1" />
                          </xs:sequence>
                          <xs:attribute name="ref" use="optional" type="xs:string" />
                        </xs:complexType>
                      </xs:element>
                    </xs:sequence>
                    <xs:attribute name="name" type="xs:string" use="required" />
                  </xs:complexType>
                </xs:element>
              </xs:sequence>
            </xs:complexType>
          </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>  
</xs:schema>
Mitchel Sellers
Thank you for your effort Mitchel, but the key point of the question was how to set a co-occurrence constraint to the ref/primitive pair. Apparently my bad English failed me in expressing that. So I corrected the question accordingly.
Maxim Vladimirsky
Dang! You got the one last piece that I was working on, and since I wasn't sure of your problem area, I stopped working at it. Great solution!
Mitchel Sellers
+3  A: 

After searching on the Internet and digging in some books I figured out how to implement that.

First of all we need to define a generic type which accommodates all attributes and elements from both kinds of the primitive element. It is assumed that the definition element is defined somewhere else.

<xs:complexType name="primitive" abstract="true">
    <xs:sequence>
        <xs:element ref="definition" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="name" type="xs:Name" />
    <xs:attribute name="ref" type="xs:Name" />
</xs:complexType>

Then we define two primitive subtypes to be used in the primitive-list and composite respectively.

<xs:complexType name="public-primitive">
    <xs:complexContent>
        <xs:restriction base="primitive">
            <xs:sequence>
                <xs:element ref="definition" minOccurs="1" maxOccurs="unbounded" />
            </xs:sequence>
            <xs:attribute name="name" type="xs:Name" use="required" />
            <xs:attribute name="ref" use="prohibited" />
        </xs:restriction>
    </xs:complexContent>
</xs:complexType>

<xs:complexType name="private-primitive">
    <xs:complexContent>
        <xs:restriction base="primitive">
            <xs:sequence>
                <xs:element ref="definition" minOccurs="0" maxOccurs="unbounded" />
            </xs:sequence>
            <xs:attribute name="name" use="prohibited" />
        </xs:restriction>
    </xs:complexContent>
</xs:complexType>

Now we can define the primitive-list and composite elements in terms of these complex types as follows:

<xs:element name="primitive-list">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="primitive" type="public-primitive" maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

<xs:element name="composite">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="primitive" type="private-primitive" maxOccurs="unbounded">
                <xs:key name="definition-ref--co-occurrence--constraint">
                    <xs:selector xpath="." />
                    <xs:field xpath="definition|@ref" />
                </xs:key>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

Let's take a look at the original schema requirements and see how they are enforced:

  • If a primitive element is specified inside a primitive-list element, then it should contain the name attribute and the embedded definition element, but not the ref attribute.

This requirement is enforced by the definition of the public-primitive type alone.

  • If a primitive element is specified in the composite element, then it should contain either the ref attribute or the definition element. The name is allowed in neither cases.

This requirement is enforced by the definition of the private-primitive type and by the xs:key element specified in the primitive element defined inside of the composite element. The xs:key guaranties that either ref or definition is present but not both.

Maxim Vladimirsky
This is really helpful. It's a shame that the xs:key restriction is attached to the element and not to a type, since that means you can't re-use it for other elements.
Chris Conway
A: 

hi, may i know how does the syntax look like in order to query by a complexType name of "PrimitiveType" to display the element name and type of "definitions" using XPath Expressions.

k9999