4

I am trying to create an XSD Schema for an XML document I have received however there are two elements staff which have the same name.

Is there any way to create a schema for this XML even though there are two elements with the same name?

XML:

<contacts>
    <staff count="248" pagesize="284">
    <staff id="1231">
    <Forename>test</Forename>
    <Surname>test</Surname>
    <DateOfBirth>0000-00-00</DateOfBirth>
    <Gender/>
    <Address1/>
    <Address2/>
    <Town/>
    <County/>
    <Telephone/>
    <Mobile/>
    <Email/>
    <Created>0000-06-18 09:46:32</Created>
    <CreatedBy>test</CreatedBy>
    <Updated>2000-06-18 09:46:32</Updated>
    <UpdatedBy>test</UpdatedBy>
    <Archived>0000-00-00 00:00:00</Archived>
    <ArchivedBy/>
    <Postcode/>
    <Age>0</Age>
    <RestrictedRecord>0</RestrictedRecord>
    <Disability_S_24/>
    <Ethnicity_S_25/>
    <Type>8</Type>
    <PersonID>1231</PersonID>
    <TypeName>staff</TypeName>
    </staff>
</staff>
</contacts>

Schema so far:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="contacts">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="staff"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="staff">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded" ref="staff"/>
            </xs:sequence>
            <xs:attribute name="count" use="required" type="xs:integer"/>
            <xs:attribute name="pagesize" use="required" type="xs:integer"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="staff">
        <xs:complexType>
            <xs:all  >
                <xs:element ref="Forename" minOccurs="0"/>
                <xs:element ref="Surname" minOccurs="0"/>
                <xs:element ref="DateOfBirth" minOccurs="0"/>
                <xs:element ref="Gender" minOccurs="0"/>
                <xs:element ref="Address1" minOccurs="0"/>
                <xs:element ref="Address2" minOccurs="0"/>
                <xs:element ref="Town" minOccurs="0"/>
                <xs:element ref="County" minOccurs="0"/>
                <xs:element ref="Telephone" minOccurs="0"/>
                <xs:element ref="Mobile" minOccurs="0"/>
                <xs:element ref="Email" minOccurs="0"/>
                <xs:element ref="Created" minOccurs="0"/>
                <xs:element ref="CreatedBy" minOccurs="0"/>
                <xs:element ref="Updated" minOccurs="0"/>
                <xs:element ref="UpdatedBy" minOccurs="0"/>
                <xs:element ref="Archived" minOccurs="0"/>
                <xs:element ref="ArchivedBy" minOccurs="0"/>
                <xs:element ref="Postcode" minOccurs="0"/>
                <xs:element ref="Age" minOccurs="0"/>
                <xs:element ref="RestrictedRecord" minOccurs="0"/>
                <xs:element ref="Disability_S_24" minOccurs="0"/>
                <xs:element ref="Ethnicity_S_25" minOccurs="0"/>
                <xs:element ref="Education_V_2" minOccurs="0"/>
                <xs:element ref="EmploymentTrainingStatus_V_1" minOccurs="0"/>
                <xs:element ref="Type" minOccurs="0"/>
                <xs:element ref="PersonID" minOccurs="0"/>
                <xs:element ref="TypeName" minOccurs="0"/>
            </xs:all>
            <xs:attribute name="id" use="required" type="xs:integer"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="Forename" type="xs:string"/>
    <xs:element name="Surname" type="xs:string"/>
    <xs:element name="DateOfBirth" type="xs:NMTOKEN"/>
    <xs:element name="Gender" type="xs:string"/>
    <xs:element name="Address1" type="xs:string"/>
    <xs:element name="Address2" type="xs:string"/>
    <xs:element name="Town" type="xs:string"/>
    <xs:element name="County" type="xs:string"/>
    <xs:element name="Telephone" type="xs:string"/>
    <xs:element name="Mobile" type="xs:string"/>
    <xs:element name="Email" type="xs:string"/>
    <xs:element name="Created" type="xs:string"/>
    <xs:element name="CreatedBy" type="xs:NCName"/>
    <xs:element name="Updated" type="xs:string"/>
    <xs:element name="UpdatedBy" type="xs:NCName"/>
    <xs:element name="Archived" type="xs:string"/>
    <xs:element name="ArchivedBy" type="xs:string"/>
    <xs:element name="Postcode" type="xs:string"/>
    <xs:element name="Age" type="xs:integer"/>
    <xs:element name="RestrictedRecord" type="xs:integer"/>
    <xs:element name="Disability_S_24">
        <xs:complexType/>
  </xs:element>
    <xs:element name="Ethnicity_S_25">
        <xs:complexType/>
    </xs:element>
    <xs:element name="Type" type="xs:integer"/>
    <xs:element name="PersonID" type="xs:integer"/>
    <xs:element name="TypeName" type="xs:NCName"/>
</xs:schema>
Dan Field
  • 20,885
  • 5
  • 55
  • 71
Andrew Hoban
  • 43
  • 1
  • 4

3 Answers3

2

If two sibling elements have the same name, then there's a rule in XSD ("element declarations consistent") that says they must also have the same type.

However, if elements appear in different places in the XML, not as siblings, (for example staff/staff versus contacts/staff) then they can have different types; this can be achieved using local element declarations.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
1

A. If you have control over both the XSD and the XML, here are options for having two staff elements with different content models in your XML:

  1. Use namespaces to differentiate them.
  2. Rename one of them.

B. If you have control over the XSD but not the XML, here are some options:

  1. Use local definitions. (Two globally defined elements cannot have the same name, but it's fine if one or both are locally defined.) [Recommended, if you really want to use the same name.]
  2. Merge the two content models such that components in common are required and components that are distinct are optional. (If context differentiates the two content models, optionally add XSD 1.1 assertions to enforce the differentiation.)

Here is a completely corrected XSD using option B.1:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
           elementFormDefault="qualified">
  <xs:element name="contacts">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="staff"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="staff">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="staff" maxOccurs="unbounded">
          <xs:complexType>
            <xs:all>
              <xs:element ref="Forename" minOccurs="0"/>
              <xs:element ref="Surname" minOccurs="0"/>
              <xs:element ref="DateOfBirth" minOccurs="0"/>
              <xs:element ref="Gender" minOccurs="0"/>
              <xs:element ref="Address1" minOccurs="0"/>
              <xs:element ref="Address2" minOccurs="0"/>
              <xs:element ref="Town" minOccurs="0"/>
              <xs:element ref="County" minOccurs="0"/>
              <xs:element ref="Telephone" minOccurs="0"/>
              <xs:element ref="Mobile" minOccurs="0"/>
              <xs:element ref="Email" minOccurs="0"/>
              <xs:element ref="Created" minOccurs="0"/>
              <xs:element ref="CreatedBy" minOccurs="0"/>
              <xs:element ref="Updated" minOccurs="0"/>
              <xs:element ref="UpdatedBy" minOccurs="0"/>
              <xs:element ref="Archived" minOccurs="0"/>
              <xs:element ref="ArchivedBy" minOccurs="0"/>
              <xs:element ref="Postcode" minOccurs="0"/>
              <xs:element ref="Age" minOccurs="0"/>
              <xs:element ref="RestrictedRecord" minOccurs="0"/>
              <xs:element ref="Disability_S_24" minOccurs="0"/>
              <xs:element ref="Ethnicity_S_25" minOccurs="0"/>
              <xs:element ref="Education_V_2" minOccurs="0"/>
              <xs:element ref="EmploymentTrainingStatus_V_1" minOccurs="0"/>
              <xs:element ref="Type" minOccurs="0"/>
              <xs:element ref="PersonID" minOccurs="0"/>
              <xs:element ref="TypeName" minOccurs="0"/>
            </xs:all>
            <xs:attribute name="id" use="required" type="xs:integer"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="count" use="required" type="xs:integer"/>
      <xs:attribute name="pagesize" use="required" type="xs:integer"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="Forename" type="xs:string"/>
  <xs:element name="Surname" type="xs:string"/>
  <xs:element name="DateOfBirth" type="xs:NMTOKEN"/>
  <xs:element name="Gender" type="xs:string"/>
  <xs:element name="Address1" type="xs:string"/>
  <xs:element name="Address2" type="xs:string"/>
  <xs:element name="Town" type="xs:string"/>
  <xs:element name="County" type="xs:string"/>
  <xs:element name="Telephone" type="xs:string"/>
  <xs:element name="Mobile" type="xs:string"/>
  <xs:element name="Email" type="xs:string"/>
  <xs:element name="Created" type="xs:string"/>
  <xs:element name="CreatedBy" type="xs:NCName"/>
  <xs:element name="Updated" type="xs:string"/>
  <xs:element name="UpdatedBy" type="xs:NCName"/>
  <xs:element name="Archived" type="xs:string"/>
  <xs:element name="ArchivedBy" type="xs:string"/>
  <xs:element name="Postcode" type="xs:string"/>
  <xs:element name="Age" type="xs:integer"/>
  <xs:element name="RestrictedRecord" type="xs:integer"/>
  <xs:element name="Disability_S_24">
    <xs:complexType/>
  </xs:element>
  <xs:element name="Ethnicity_S_25">
    <xs:complexType/>
  </xs:element>
  <xs:element name="Education_V_2">
    <xs:complexType/>
  </xs:element>
    <xs:element name="EmploymentTrainingStatus_V_1">
    <xs:complexType/>
  </xs:element>
  <xs:element name="Type" type="xs:integer"/>
  <xs:element name="PersonID" type="xs:integer"/>
  <xs:element name="TypeName" type="xs:NCName"/>
</xs:schema>

This XSD will successfully validate your XML.

kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • Thanks for the reply. I have no control over the XML document itself, I only have API access to gather the data. If i just rename the elements in the Schema, will that still work? – Andrew Hoban Jun 20 '16 at 13:17
  • No, sorry, renaming elements unilaterally in the XSD won't work. Answer updated to differentiate between whether you have control over the XML or not. – kjhughes Jun 20 '16 at 13:25
  • Would you be able to provide an example of merging the two content models at all? I am trying many things but having no success. – Andrew Hoban Jun 20 '16 at 14:12
  • I think in your case, using a local definition for one of the `staff` elements would work best. I've added an XSD that will validate your XML without having to change it. – kjhughes Jun 20 '16 at 15:02
  • Thank you for the example. When attempting to run this i receive the error message: expected end of tag 'staff' [CODE:10386] file: TreeMapper.cpp, line 763 – Andrew Hoban Jun 20 '16 at 15:50
  • The XSD in my answer successfully validates the XML in your question and shows how you can validate XML with multiple elements with the same name. I would ask that you [**accept**](http://meta.stackoverflow.com/q/5234/234215) this answer if it's helped, and ask a new question with added details regarding the context in which you're using this XML/XSD and the tools that involved TreeMapper.cpp. Thanks. – kjhughes Jun 20 '16 at 16:17
1

Your formatting threw me off there - I initially corrected it in your question but changed my mind as I think it's a relevant detail to your question. You don't actually have two sibling staff elements (I noticed this when I was looking for the closing tags), you have a child that has the same name as its parent. That's fine. Here's your reformatted XML:

<contacts>
    <staff count="248" pagesize="284">
        <staff id="1231">
            <Forename>test</Forename>
            <Surname>test</Surname>
            <DateOfBirth>0000-00-00</DateOfBirth>
            <Gender/>
            <Address1/>
            <Address2/>
            <Town/>
            <County/>
            <Telephone/>
            <Mobile/>
            <Email/>
            <Created>0000-06-18 09:46:32</Created>
            <CreatedBy>test</CreatedBy>
            <Updated>2000-06-18 09:46:32</Updated>
            <UpdatedBy>test</UpdatedBy>
            <Archived>0000-00-00 00:00:00</Archived>
            <ArchivedBy/>
            <Postcode/>
            <Age>0</Age>
            <RestrictedRecord>0</RestrictedRecord>
            <Disability_S_24/>
            <Ethnicity_S_25/>
            <Type>8</Type>
            <PersonID>1231</PersonID>
            <TypeName>staff</TypeName>
        </staff>
    </staff>
</contacts>

That said, you can't construct the schema the way you have it right now - think about it, which staff element is that ref referring to? How could any XSD parser know which one it is?

I think the easiest way to correct your schema would be to do the following:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="contacts">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="staff"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="staff">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded" name="staff" type="staffInner"/>
            </xs:sequence>
            <xs:attribute name="count" use="required" type="xs:integer"/>
            <xs:attribute name="pagesize" use="required" type="xs:integer"/>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="staffInner">
        <xs:all>
         ....

I'd pick a better name than staffInner if I had a bit more context - maybe something like individualStaffMember? I also might end up changing your other ref to use a type instead for consistency.

Also, are you sure you want xs:all and not just xs:sequence? If you keep xs:all, the minOccurs="0" is redundant on all those child elements. If you mean to have xs:sequence (which I suspect is probably the case), you do need it there - but beware, because xs:all is going to allow the elements to appear in any order...

Dan Field
  • 20,885
  • 5
  • 55
  • 71