Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XML schema still allowing duplicate id's with unique

Tags:

xml

xsd

I am trying to design an XML schema for books where a unique ID needs to be specified for each book entry. However it just doesn't seem to work. Below is the XSD i am using,

<?xml version="1.0"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           elementFormDefault="qualified"
           attributeFormDefault="unqualified">

  <xs:element name="BookShelf">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Description" type="xs:string" minOccurs="0"/>
        <xs:element name="Shelf" type="ShelfType" minOccurs="1" maxOccurs="10"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:complexType name="ShelfType">
    <xs:sequence>
      <xs:element ref="Book" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>


<xs:element name="Book">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Title" type="xs:token"/>
      <xs:element name="Language" type="xs:language"/>
    </xs:sequence>
    <xs:attribute name="id" type="xs:string" use="required"/>
  </xs:complexType>
  <xs:unique name="unique-bookId">
    <xs:selector xpath="Book"/>
    <xs:field xpath="@id"/>
  </xs:unique>
</xs:element>
</xs:schema>

The XML I am trying to validate with this is,

<?xml version="1.0"?>

<BookShelf>
    <Description>My bookshelf</Description>
    <Shelf>
        <Book id="1">
            <Title>Seitsemän veljestä</Title>
            <Language>fi</Language>
        </Book>
        <Book id="1">
            <Title>Another title</Title>
            <Language>en</Language>
        </Book>
    </Shelf>
</BookShelf>

which is validating fine even though it should not (I've used the same id for 2 entries). I'm pretty new at XML and would appreciate if someone could please point out what I am doing wrong here?

like image 524
source.rar Avatar asked Feb 15 '13 11:02

source.rar


People also ask

What is the limitation of using XS ID in XML schema?

Restrictions. Applications that need to maintain a level of compatibility with DTDs should not use this datatype for elements but should reserve it for attributes. The lexical domain ( xs:NCName ) of this datatype doesn't allow the definition of numerical identifiers or identifiers containing whitespaces.

What is xs in XML schema?

1.1 The Schema Namespace ( xs ) The XML representation of schema components uses a vocabulary identified by the namespace name http://www.w3.org/2001/XMLSchema . For brevity, the text and examples in this specification use the prefix xs: to stand for this namespace; in practice, any prefix can be used.

How do you make an attribute unique in XSD?

You can create a Unique constraint within an XSD using the <xs:unique> element. The selector and field pair specify the data that must be unique, the XPath expressions are relative to the parent element (Directory).

What is the purpose of schema select one?

The purpose of an XML Schema is to define the legal building blocks of an XML document: the elements and attributes that can appear in a document. the number of (and order of) child elements. data types for elements and attributes.


2 Answers

You've got the <xs:unique> in the wrong place - it needs to be inside the definition of the ancestor element within which the Book elements should be unique, not in the Book element definition itself. The following would force Book ids to be unique within each shelf, but would allow the same ID on different shelves:

  <xs:element name="BookShelf">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Description" type="xs:string" minOccurs="0"/>
        <xs:element name="Shelf" type="ShelfType" minOccurs="1" maxOccurs="10">
          <xs:unique name="unique-bookId">
            <xs:selector xpath="Book"/><!-- selects books on this shelf -->
            <xs:field xpath="@id"/>
          </xs:unique>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

If instead you want the IDs to be globally unique across all shelves then put the unique constraint at the BookShelf level and adjust the selector appropriately:

  <xs:element name="BookShelf">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Description" type="xs:string" minOccurs="0"/>
        <xs:element name="Shelf" type="ShelfType" minOccurs="1" maxOccurs="10"/>
      </xs:sequence>
    </xs:complexType>
    <xs:unique name="unique-bookId">
      <xs:selector xpath="Shelf/Book"/><!-- selects books on all shelves -->
      <xs:field xpath="@id"/>
    </xs:unique>
  </xs:element>

For future reference, note that if your schema had a targetNamespace then those selectors would not work as-is, because unprefixed names in selector XPaths always mean "no namespace". You would need to add xmlns:tns="<target namespace URI>" to your xs:schema element and then use a selector of tns:Shelf/tns:Book.

like image 184
Ian Roberts Avatar answered Oct 19 '22 19:10

Ian Roberts


Do you specifically need to use digits as ID values? If not, one possibility is using xs:ID as the id attribute type instead of xs:string:

<xs:attribute name="id" type="xs:ID" use="required"/>

That way only unique XML identifiers are allowed as values of the id attribute. A valid XML identifier can't start with a digit, however, so you'll have to change your ID type to something like id-1, for example.

like image 23
Eero Helenius Avatar answered Oct 19 '22 19:10

Eero Helenius