The goal is to constrain BEST_FRIEND_ID
to have a value of an existing FRIEND_ID
attribute in this XML:
friends.xml
<?xml version="1.0"?>
<FRIENDS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="friends.xsd">
<FRIEND FRIEND_ID="1">
<NAME>John</NAME>
<BEST_FRIEND_ID>2</BEST_FRIEND_ID>
</FRIEND>
<FRIEND FRIEND_ID="2">
<NAME>Kate</NAME>
<BEST_FRIEND_ID>1</BEST_FRIEND_ID>
</FRIEND>
</FRIENDS>
E.g. for given XML, validation should fail if I change a value of any BEST_FRIEND_ID
element to 3
.
friends.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning">
<xs:complexType name="FriendsType">
<xs:sequence>
<xs:element ref="FRIEND" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FriendType">
<xs:sequence>
<xs:element ref="NAME"/>
<xs:element ref="BEST_FRIEND_ID"/>
</xs:sequence>
<xs:attribute name="FRIEND_ID" type="xs:integer"/>
</xs:complexType>
<xs:element name="FRIENDS" type="FriendsType"/>
<xs:element name="FRIEND" type="FriendType"/>
<xs:element name="NAME" type="xs:string"/>
<xs:element name="BEST_FRIEND_ID" type="xs:integer"/>
</xs:schema>
My idea was to use power of asserts (XSD 1.1). This is what I've figured out so far
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1">
<xs:complexType name="FriendsType">
<xs:sequence>
<xs:element ref="FRIEND" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FriendType">
<xs:sequence>
<xs:element ref="NAME"/>
<xs:element ref="BEST_FRIEND_ID"/>
</xs:sequence>
<xs:attribute name="FRIEND_ID" type="xs:integer"/>
</xs:complexType>
<xs:complexType name="IdConstrain">
<xs:simpleContent>
<xs:extension base="xs:integer">
<xs:assert test="$value = @FRIEND_ID"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:element name="FRIENDS" type="FriendsType"/>
<xs:element name="FRIEND" type="FriendType"/>
<xs:element name="NAME" type="xs:string"/>
<xs:element name="BEST_FRIEND_ID" type="IdConstrain"/>
</xs:schema>
and the validation fails:
Severity: error Description: cvc-assertion: Assertion evaluation ('$value = @FRIEND_ID') for element 'BEST_FRIEND_ID' on schema type 'IdConstrain' did not succeed. Start location: 6:10 End location: 6:25 URL: http://www.w3.org/TR/xmlschema11-1/#cvc-assertion
I have also tried with something like BEST_FRIEND_ID[contains(., @FRIEND_ID)]
but it does not work as well.
You should use the following to check that every best friend id is an existing friend id. You should also check that the bestfriend id is not equal to itself:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
vc:minVersion="1.1">
<xs:complexType name="FriendsType">
<xs:sequence>
<xs:element ref="FRIEND" maxOccurs="unbounded"/>
</xs:sequence>
<xs:assert test="every $bfid in FRIEND/BEST_FRIEND_ID
satisfies $bfid = (FRIEND/@FRIEND_ID)"/>
<!-- Optional check best friend is not equals to itself -->
<xs:assert test="every $fr in FRIEND
satisfies $fr/@FRIEND_ID != $fr/BEST_FRIEND_ID"/>
</xs:complexType>
<xs:complexType name="FriendType">
<xs:sequence>
<xs:element ref="NAME"/>
<xs:element ref="BEST_FRIEND_ID"/>
</xs:sequence>
<xs:attribute name="FRIEND_ID" type="xs:integer"/>
</xs:complexType>
<xs:element name="FRIENDS" type="FriendsType"/>
<xs:element name="FRIEND" type="FriendType"/>
<xs:element name="NAME" type="xs:string"/>
<xs:element name="BEST_FRIEND_ID" type="xs:integer"/>
</xs:schema>
You could also check is bestfriend id is diferent to the friend id inside FRIEND
tag instead of the FRIENDS
tag.
<xs:complexType name="FriendType">
<xs:sequence>
<xs:element ref="NAME"/>
<xs:element ref="BEST_FRIEND_ID"/>
</xs:sequence>
<xs:attribute name="FRIEND_ID" type="xs:integer"/>
<xs:assert test="@FRIEND_ID != BEST_FRIEND_ID"/>
</xs:complexType>