开发者

Creating a 'flexible' XML schema

I need to create a schema for an XML file that is pretty flexible. It has to meet the following requirements:

  1. Validate some elements that we require to be present, and know the exact structure of
  2. Validate some elements that are optional, and we know the exact structure of
  3. Allow any other elements
  4. Allow them in any order

Quick example:

XML

<person>
    <age></age>
    <lastname></lastname>
    <height></height>
</person>

My attempt at an XSD:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="person">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="firstname" minOccurs="0" type="xs:string"/>
        <xs:element name="lastname" type="xs:string"/>
        <xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Now my XSD satisfies requirements 1 and 3. It is not a valid schema however, if both firstname and lastname were optional, so it doesn't satisfy requirement 2, and the order is fixed, which fails requirement 4.

Now all I need is somethin开发者_C百科g to validate my XML. I'm open to suggestions on any way of doing this, either programmatically in .NET 3.5, another type of schema etc.

Can anyone think of a solution to satisfy all 4 requirements?


Your requirement number 3 cannot be addressed if the name elements are optional because your schema would then violate the unique particle attribution rule (basically, the processor won't know whether to validate firstname against firstname or against any).

You are not restricted to a single schema as far as validation is concerned. If you are ok with using two schemas in different namespaces, you can do this:

Schema one - allow anything:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="document">
    <xs:complexType>
      <xs:sequence>
        <xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Schema two - add specific validation for some elements:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://other" xmlns="http://other">
    <xs:element name="firstname" type="xs:string"/>
    <xs:element name="lastname" type="xs:string"/>
</xs:schema>

Then make sure your instance document's xsi:include references both schemas.


I was thinking about this problem a lot today. I was considering how hard this xs:all rule makes it for XML databases to store documents that have unstructured "CMS" like data, while also validating the data.

Then it occurred to me that XHTML allows for very flexible arrangements of nested elements in whatever order you need to mark a page up with.

So here's an excerpt from the XHTML 1.1 schema:

  <xs:group name="InlForm.class">
    <xs:choice>
      <xs:element ref="input"/>
      <xs:element ref="select"/>
      <xs:element ref="textarea"/>
      <xs:element ref="label"/>
      <xs:element ref="button"/>
    </xs:choice>
  </xs:group>

  <xs:group name="Inline.extra">
    <xs:choice/>
  </xs:group>

  <xs:group name="Ruby.class">
    <xs:sequence>
      <xs:element ref="ruby"/>
    </xs:sequence>
  </xs:group>

  <!--
   Inline.class includes all inline elements,
   used as a component in mixes
  -->
  <xs:group name="Inline.class">
    <xs:choice>
      <xs:group ref="InlStruct.class"/>
      <xs:group ref="InlPhras.class"/>
      <xs:group ref="InlPres.class"/>
      <xs:group ref="I18n.class"/>
      <xs:group ref="Anchor.class"/>
      <xs:group ref="InlSpecial.class"/>
      <xs:group ref="InlForm.class"/>
      <xs:group ref="Ruby.class"/>
      <xs:group ref="Inline.extra"/>
    </xs:choice>
  </xs:group>

  <xs:group name="Heading.class">
    <xs:choice>
      <xs:element ref="h1"/>
      <xs:element ref="h2"/>
      <xs:element ref="h3"/>
      <xs:element ref="h4"/>
      <xs:element ref="h5"/>
      <xs:element ref="h6"/>
    </xs:choice>
  </xs:group>

They essentially nest choices of groups, recursively. I imagine the person that wrote this is living out the rest of their days in a secure institution receiving forced medication several times a day.

I hope this helps. I think this illustrates how super-flexi schemas are 'really' done in XSD 1.0.

Edit - it works! You can make a 'master' group of all the other groups and use this example ListItem element definition to allow continuously nested elements in any order. Ensure ListItem is also included within a group so the recursion works.

  <xs:element name="ListItem">
    <xs:complexType>
      <xs:sequence>
        <xs:group ref="content:any.mix"  minOccurs="1" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

So my any.mix group looks like this:

  <xs:group name="any.mix">
    <xs:choice>      
      <xs:group ref="content:item.class" />
      <xs:group ref="content:media.class" />
      <xs:group ref="content:data.class" />
      <xs:group ref="content:list.class" />      
    </xs:choice>
  </xs:group>

And each of those "class" groups contain yet more choices of groups, and so on and so on until they eventually hit the elements, the leaf level actual tags if you like.

The groups themselves should not have circular references; the 'trick' is in the unbounded occurrences of the any.mix group, i.e. its a tree of choices with an unlimited root choice.

Luke

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜