Delete
Delete

Reputation: 299

Making Element Values Unique

I have a small XML project I'm doing where I am defining a schema for a chess game state made in XML. This includes creating chess piece elements and creating child elements for these pieces that reference their position in PGN (positional game notation).

The document continues by defining King, Queen etc and their position(s). How do I ensure that the value within the position tag is unique i.e., a pawn cannot have two positions at 17 as that would be having two pieces on the same square of the board.

<position>17</position>
<position>17</position>

I tried using a unique constraint but it freaked out at the fact that I have multiple position elements within the piece element. How do I make it so the value of these pieces is always unique and no two pieces can share the same position value?

EDIT: So the placement of the unique constraint works for position elements within the same piece tag, but is there a way to do it for any position throughout the whole document. Example:

<board-side name="white">
    <pieces>
        <piece name="Pawn" symbol="p">
            <position>12</position>

<board-side name="BLACK">
    <pieces>
        <piece name="Pawn" symbol="P">
            <position>17</position>

Both of the above XML fragments are within their own board-side tags. Is there a way to make it so that if I change the position of the BLACK board side to 12, it will throw a unique constraint error, or does unique only work for multiple elements within the same tag?

EDIT 2: Whole Schema

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema id="chess_schema" xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="ident">
        <xs:restriction base="xs:string">
            <xs:enumeration value="P" />
            <xs:enumeration value="p" />
            <xs:enumeration value="Q" />
            <xs:enumeration value="q" />
            <xs:enumeration value="R" />
            <xs:enumeration value="r" />
            <xs:enumeration value="N" />
            <xs:enumeration value="n" />
            <xs:enumeration value="B" />
            <xs:enumeration value="b" />
            <xs:enumeration value="K" />
            <xs:enumeration value="k" />
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="game-state">
        <xs:complexType>
            <xs:sequence>

                <xs:element name="board-state">
                    <xs:complexType>
                        <xs:sequence>

                            <xs:element name="board-side" minOccurs="2" maxOccurs="2">
                                <xs:complexType>
                                    <xs:sequence>

                                        <xs:element name="pieces">
                                            <xs:complexType>
                                                <xs:sequence>

                                                    <xs:element name="piece" maxOccurs="6">
                                                        <xs:complexType>
                                                            <xs:sequence>

                                                                <xs:element name="position" minOccurs="0" maxOccurs="8" >
                                                                    <xs:simpleType>
                                                                        <xs:restriction base="xs:int">
                                                                            <xs:minInclusive value="11" />
                                                                            <xs:maxInclusive value="88" />
                                                                        </xs:restriction>
                                                                    </xs:simpleType>
                                                                </xs:element>

                                                            </xs:sequence>
                                                            <xs:attribute name="symbol" type="ident" />
                                                            <xs:attribute name="name" type="xs:string" />
                                                        </xs:complexType>
                                                    </xs:element>

                                                </xs:sequence>
                                            </xs:complexType>
                                        </xs:element>

                                        <xs:element name="taken-pieces" maxOccurs="1" minOccurs="0" type="xs:string" />

                                    </xs:sequence>
                                    <xs:attribute name="name" type="xs:string" />
                                </xs:complexType>
                            </xs:element>

                        </xs:sequence>
                    </xs:complexType>
                </xs:element>

            </xs:sequence>
        </xs:complexType>

        <xs:unique name="uniquePos">
            <xs:selector xpath="board-side/pieces/piece/position" />
            <xs:field xpath="." />
        </xs:unique>

    </xs:element> <!--end game-state root-->
</xs:schema>

Upvotes: 1

Views: 114

Answers (1)

helderdarocha
helderdarocha

Reputation: 23637

An uniqueness constraint guarantees that your position elements will be unique within a specified scope.

You should define the constraint in the element that represents that scope. If the scope is the piece element, for example, you can associate it to the position elements via a XPath selector relative to this context.

If you have no target namespace you can simply use it without prefixes:

<xs:element name="piece">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="position" minOccurs="0" maxOccurs="8"/>
        </xs:sequence>
    </xs:complexType>
    <xs:unique name="uniquePos">
        <xs:selector xpath="position" />
        <xs:field xpath="." />
    </xs:unique>
</xs:element>

If your schema has a target namespace, however, you must declare it with a prefix since XPath assumes that an unprefixed name belongs to no-namespace. So you need something like:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
           elementFormDefault="qualified"
           xmlns="ns1" xmlns:chess="ns1" targetNamespace="ns1">  ...

(you don't really need to duplicate it as the default namespace, unless you also refer to unqualified types somewhere else, but you need the prefixed one).

Then you can prefix your XPath selectors:

<xs:unique name="uniquePos">
    <xs:selector xpath="chess:position" />
    <xs:field xpath="." />
</xs:unique>

UPDATE considering your edition.

If you want to increase the scope where your uniqueness constraint is valid, you just have to place the declaration in that scope. For example, if you want the positions to be unique within the entire game-state element, you can declare it there:

<xs:element name="game-state">
    ...
    <xs:unique name="uniquePos">
        <xs:selector xpath="/board-state/board-side/pieces/piece/position" />
        <xs:field xpath="." />
    </xs:unique>
</xs:element>

This will cause a validation error if there are two position elements with the same value in any context below game-state. You can also put it in other places, as long as the XPath expression is valid in that context. For example, in board-state it would be:

<xs:element name="board-state">
    ...
    <xs:unique name="uniquePos">
        <xs:selector xpath="board-side/pieces/piece/position" />
        <xs:field xpath="." />
    </xs:unique>
</xs:element>

Upvotes: 2

Related Questions