Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to control references between elements in XML

Tags:

reference

xml

xsd

In my previous question (How create references between elements in XML) I asked how to create, in an XML Schema, references between elements.

Ok, but now I noticed something. For example, defining these three types of vehicles:

<Car id="car1">
    <plate>AAA</plate>
    <mark>Peugeot</mark>
    <model>206</model>
</Car>

<Truck id="truck1">
    <plate>BBB</plate>
    <mark>Scania</mark>
    <model>X1</model>
</Truck>

<Trailer id="trailer1">
    <plate>CCC</plate>
    <mark>Scania</mark>
    <model>T1</model>
</Trailer>

... I can now define complex vehicle, for example:

<TrailerTruck id="tt1">
    <Car refid="car1"/>
    <Trailer refid="trailer1"/>
</TrailerTruck>

<TrailerTruck id="tt2">
    <Truck refid="truck1"/>
    <Trailer refid="trailer1"/>
</TrailerTruck>

Okay, it works, but I noticed that - unfortunately - also allowed such a thing:

<TrailerTruck id="tt3_WRONG">
    <Truck refid="trailer1"/>   <!-- an element "Truck" should not
    refer to a trailer!! Should be able to refer only to truck1 -->
    <Trailer refid="car1"/>     <!-- like before, an element Trailer
    should be able to refer only trailer1 -->
</TrailerTruck>

<TrailerTruck id="tt4_WRONG">
    <Car refid="truck1"/>       <!-- an element **Car** should not
    refer to a Truck !! -->
    <Trailer refid="trailer1"/>
</TrailerTruck>

So, if I put incorrect references, is not shown any error. Instead, I would like to be notified. I want to insert some kind of restriction or control. But how?


Now I show you my current implementation.

VehicleXMLSchema.xsd:

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

    <xs:element name="Fleet" type="FleetType"/>


    <xs:complexType name="FleetType">
        <xs:choice maxOccurs="unbounded">
            <xs:element ref="Car" />
            <xs:element ref="Truck" />
            <xs:element ref="Trailer" />
            <xs:element ref="TrailerTruck" />
        </xs:choice>
    </xs:complexType>


    <xs:complexType name="VehicleType" abstract="true">
        <xs:sequence minOccurs="0">
            <xs:element name="plate" type="xs:string" minOccurs="1" />
            <xs:element name="mark" type="xs:string" minOccurs="1" />
            <xs:element name="model" type="xs:string" minOccurs="1" />
        </xs:sequence>
    </xs:complexType>




    <!-- SimpleVehicle and ComplexVehicle -->

    <xs:complexType name="SimpleVehicle">
        <xs:complexContent>
            <xs:extension base="VehicleType">
                <xs:attribute name="id" type="xs:ID"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="ComplexVehicle">
        <xs:complexContent>
            <xs:extension base="VehicleType">
                <xs:attribute name="refid" type="xs:IDREF"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>




    <!-- Car, Truck, Trailer -->

    <xs:element name="Car" type="SimpleVehicle" />
    <xs:element name="Truck" type="SimpleVehicle"/>
    <xs:element name="Trailer" type="SimpleVehicle"/>




    <!-- TrailerTruck -->

    <xs:group name="DrivingPart">
        <xs:choice>
            <xs:element name="Car" type="ComplexVehicle" />
            <xs:element name="Van" type="ComplexVehicle"/>
            <xs:element name="Truck" type="ComplexVehicle"/>
        </xs:choice>
    </xs:group>

    <xs:element name="TrailerTruck">
        <xs:complexType>
            <xs:sequence>
                <xs:group ref="DrivingPart" minOccurs="1" maxOccurs="1"/>
                <xs:element name="Trailer" type="ComplexVehicle" minOccurs="1" maxOccurs="1"/>
            </xs:sequence>
            <xs:attribute name="id" type="xs:ID"/>
        </xs:complexType>
    </xs:element>

</xs:schema>

Shipper1.xml:

<Fleet shipperName="Shipper1" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="VehicleXMLSchema.xsd">

    <Car id="car1">
        <plate>AAA</plate>
        <mark>Peugeot</mark>
        <model>206</model>
    </Car>

    <Truck id="truck1">
        <plate>DDD</plate>
        <mark>Scania</mark>
        <model></model>
    </Truck>

    <Trailer id="trailer1">
        <plate>EEE</plate>
        <mark>Scania</mark>
        <model></model>
    </Trailer>


    <TrailerTruck id="trailerTruck1">
        <Car refid="car1" />
        <Trailer refid="trailer1" />
    </TrailerTruck>

    <TrailerTruck id="trailerTruck2_WRONG">
        <Truck refid="car1" />
        <Trailer refid="trailer1" />
    </TrailerTruck>

    <TrailerTruck id="trailerTruck3_WRONG">
        <Truck refid="truck1" />
        <Trailer refid="car1" />
    </TrailerTruck>

</Fleet>
like image 484
Gioce90 Avatar asked Apr 01 '15 12:04

Gioce90


1 Answers

I think you're looking for referential integrity; you really need xsd:key/xsd:keyref. ID/IDREF are in the XSD spec for backward compatibility with DTDs; I wouldn't use them in XSDs.

This is your modified XSD:

<?xml version="1.0" encoding="utf-8" ?>
<!-- XML Schema generated by QTAssistant/XSD Module (http://www.paschidev.com) -->
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="Fleet" type="FleetType">
        <xs:key name="PK_Cars">
            <xs:selector xpath="Car"/>
            <xs:field xpath="@id"/>
        </xs:key>
        <xs:key name="PK_Trucks">
            <xs:selector xpath="Truck"/>
            <xs:field xpath="@id"/>
        </xs:key>
        <xs:key name="PK_Trailers">
            <xs:selector xpath="Trailer"/>
            <xs:field xpath="@id"/>
        </xs:key>
        <xs:key name="PK_TrailerTruck">
            <xs:selector xpath="TrailerTruck"/>
            <xs:field xpath="@id"/>
        </xs:key>
        <xs:keyref name="FK_TrailerTruck2Trailers" refer="PK_Trailers">
            <xs:selector xpath="TrailerTruck/Trailer"/>
            <xs:field xpath="@refid"/>
        </xs:keyref>
        <xs:keyref name="FK_TrailerTruck2Cars" refer="PK_Cars">
            <xs:selector xpath="TrailerTruck/Car"/>
            <xs:field xpath="@refid"/>
        </xs:keyref>
        <xs:keyref name="FK_TrailerTruck2Trucks" refer="PK_Trucks">
            <xs:selector xpath="TrailerTruck/Truck"/>
            <xs:field xpath="@refid"/>
        </xs:keyref>
    </xs:element>
    <xs:complexType name="FleetType">
        <xs:choice maxOccurs="unbounded">
            <xs:element ref="Car"/>
            <xs:element ref="Truck"/>
            <xs:element ref="Trailer"/>
            <xs:element ref="TrailerTruck"/>
        </xs:choice>
        <xs:attribute name="shipperName" type="xs:string"/>
    </xs:complexType>

    <xs:complexType name="VehicleType" abstract="true">
        <xs:sequence minOccurs="0">
            <xs:element name="plate" type="xs:string" minOccurs="1"/>
            <xs:element name="mark" type="xs:string" minOccurs="1"/>
            <xs:element name="model" type="xs:string" minOccurs="1"/>
        </xs:sequence>
    </xs:complexType>
    <!-- SimpleVehicle and ComplexVehicle -->
    <xs:complexType name="SimpleVehicle">
        <xs:complexContent>
            <xs:extension base="VehicleType">
                <xs:attribute name="id" type="xs:ID"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="ComplexVehicle">
        <xs:complexContent>
            <xs:extension base="VehicleType">
                <xs:attribute name="refid" type="xs:IDREF"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>
    <!-- Car, Truck, Trailer -->
    <xs:element name="Car" type="SimpleVehicle"/>
    <xs:element name="Truck" type="SimpleVehicle"/>
    <xs:element name="Trailer" type="SimpleVehicle"/>
    <!-- TrailerTruck -->
    <xs:group name="DrivingPart">
        <xs:choice>
            <xs:element name="Car" type="ComplexVehicle"/>
<!--            <xs:element name="Van" type="ComplexVehicle"/>-->
            <xs:element name="Truck" type="ComplexVehicle"/>
        </xs:choice>
    </xs:group>
    <xs:element name="TrailerTruck">
        <xs:complexType>
            <xs:sequence>
                <xs:group ref="DrivingPart" minOccurs="1" maxOccurs="1"/>
                <xs:element name="Trailer" type="ComplexVehicle" minOccurs="1" maxOccurs="1"/>
            </xs:sequence>
            <xs:attribute name="id" type="xs:ID"/>
        </xs:complexType>
    </xs:element>
</xs:schema>

With the key/keyref, you now control your cross checks. The diagram below shows only content expanded to demonstrate:

  • Cars and Trailers PK
  • how you link trailer trucks to trailers
  • how you link your driving part (a car in this case) with the associated counterpart (a car in the top section of your XML).

enter image description here

It is consistent with referential integrity in a "traditional" database design: you got your primary keys, foreign keys. Your posted XML will now be invalid; the one below would be the valid version of it.

<Fleet shipperName="Shipper1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="VehicleXMLSchema.xsd">

    <Car id="car1">
        <plate>AAA</plate>
        <mark>Peugeot</mark>
        <model>206</model>
    </Car>

    <Truck id="truck1">
        <plate>DDD</plate>
        <mark>Scania</mark>
        <model></model>
    </Truck>

    <Trailer id="trailer1">
        <plate>EEE</plate>
        <mark>Scania</mark>
        <model></model>
    </Trailer>

    <TrailerTruck id="trailerTruck1">
        <Car refid="car1"/>
        <Trailer refid="trailer1"/>
    </TrailerTruck>

    <TrailerTruck id="trailerTruck2_WRONG">
        <Truck refid="truck1"/>
        <Trailer refid="trailer1"/>
    </TrailerTruck>

    <TrailerTruck id="trailerTruck3_WRONG">
        <Truck refid="truck1"/>
        <Trailer refid="trailer1"/>
    </TrailerTruck>

</Fleet>
like image 71
Petru Gardea Avatar answered Oct 04 '22 06:10

Petru Gardea