27

Long story short : i would like to know how to use the key/keyref from XSD to let elements have references to each other. it has to have a form of an example, using a simple xsd and an XML.

Long story : I am familiar with usage of ID/IDREF. I use those to connect elements for JAXB. I have been told repeatedly that the key/keyref construct in XSD offers enhanced flexibility for inter-element referencing. I have consulted the OReilly XML Schema book, that seems to teach everything about correct definition of key/keyref and how it is similar to the ID/IDREF (but better) and doesn't give a simple example of its use. It doesn't seem to be similar, because you define the ID as an attribute in one element and the IDREF in another element and voila. But key/keyref have to be defined in a common ancestor of the referencing and the referenced element (AFAIK)...

I use the XSD files to generate JAXB-bound Java classes with XJC

I have searched for how-tos, tutorials and examples, but google gives me scraps. same for searches on SO (also with google and inclusive search with '+' ).

In order to make everyone's lives easier i have prepared an XSD with already defined key/keyref pair as i have understood it.

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="referenced">
                <xs:complexType>
                    <xs:attribute name="id" type="xs:string" />
                </xs:complexType>
            </xs:element>
            <xs:element name="owner">
                <xs:complexType>
                    <xs:attribute name="id" type="xs:string" />
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
    <xs:key name="aKey">
        <xs:selector xpath="owner" />
        <xs:field xpath="@id" />
    </xs:key>
    <xs:keyref name="aKeyRef" refer="aKey">
        <xs:selector xpath="referenced" />
        <xs:field xpath="@id" />
    </xs:keyref>
</xs:element>

How would a piece of XML look like, with an 'owner'-element referencing a 'referenced'-element?

EDIT : applied the change proposed by Tom W, changing the xpath attribute of the key element to "owner". JAXB (XJC) still doesnt care though.

Thank you

kostja
  • 60,521
  • 48
  • 179
  • 224

5 Answers5

6

I found this thread searching for the same thing the OP was searching for - a simple usage example of the <xs:key> element. All the JAXB stuff was greek to me, and a distraction. For others finding this thread later, here's a simple example posted on MSDN a couple years after the OP asked the question here on SO:

https://msdn.microsoft.com/en-us/library/ms256101%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

In case the MSDN link changes, the breadcrumb path was:

https://msdn.microsoft.com/library then click "Switch to Library TOC view", and drill down through:

MSDN Library > .NET development > .NET Framework 4.6 and 4.5 > Development Guide > Data and Modeling > XML Standards Reference > XML Schemas (XSD) Reference > XML Schema Elements > <xsd:key> Element

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
6

There is no special syntax in the instance document. It is simply required that the keyref node matches an extant key node. Validation will tell you whether or not the key constraint is satisfied.

RE your code:

I've only just started dabbling with keys myself, but I think I've spotted an error in yours - aKey should look like:

<xs:key name="aKey">
    <xs:selector xpath="owner" />
    <xs:field xpath="@id" />
</xs:key>

Furthermore - this is a gotcha - key constraints don't recognise the default namespace. You must always prefix every part of the selector xpath with the namespace prefix of the element you're looking up. If you don't have a namespace prefix - tough, you'll need to add one. This is a limitation of the standard.

Tom W
  • 5,108
  • 4
  • 30
  • 52
  • aah, that's why there is no attribute involved. I applied the change you pointed out, but JAXB doesn't generate anything to be able to access the referenced element from the owner - the whole key/keyref part seems to have no impact on the mapping. Is there sth else i am missing maybe? – kostja Dec 20 '10 at 17:46
  • Maybe I should clarify what I'm saying in the above post. I don't know what JAXB is, but this shouldn't make any difference - what I'm trying to say is that saying ..... is all you need to express the key relationship, there is no xml syntax to specifically declare a key relationship in the instance. What I guess you mean is that you're using a framework that's supposed to function as an ORM to xml files, in which case you might expect some meaningful output, in which case, try playing around with the scope of the key and keyref by moving them around. – Tom W Dec 20 '10 at 19:50
5

The JAXB spec does not explicitly cover key/keyref. However JAXB implemenations such as EclipseLink MOXy (I'm the tech lead) have extensions for this. Our upcoming 2.2 release provides a means to specify this kind of relationship via annotations (I'll put together an example). Below is a link to how it can be done using the current release.

For more information see:

UPDATE

I was hoping to get an example included with the answer, but I am running out of time before I leave on vacation. Below is an example from our design documents that demonstrates the concept:

Community
  • 1
  • 1
bdoughan
  • 147,609
  • 23
  • 300
  • 400
4

Please, try to see this key/keyref tutorial.

The colored text is very helpful.

shogitai
  • 1,823
  • 1
  • 23
  • 50
  • This actually was incredibly useful. It sounds crazy, but the colored text was the best part about it. I do wish, however, that it used something other than AAA BBB, etc... some real life examples. – Mike Christiansen Oct 13 '18 at 00:29
3

JAXB doesn't support references by means of xs:key or xs:keyref. The specification states that these constraints may be checked during validation, but they have no effect on data.

However, you can achieve this (more or less) by using xs:ID and xs:IDREF instead. For an introduction, see the chapters 2.2.15 Referring to Another XML Element and 4.3 Assembling Data with Links (ID, IDREF) in the JAXB Tutorial by Wolfgang Laun.

For your sample XSD, this would meaning changing the element definition to

<xs:element name="root">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="referenced">
                <xs:complexType>
                    <xs:attribute name="id" type="xs:ID" />
                </xs:complexType>
            </xs:element>
            <xs:element name="owner">
                <xs:complexType>
                    <xs:attribute name="id" type="xs:IDREF" />
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

Note that identifiers with the xs:ID type are required to be globally unique in the XML document. In other words, you cannot have the same ID for two different elements in the same XML file, even if the elements are of different types.

By default, an element or attribute of type xs:IDREF binds to java.lang.Object. If you know in advance what type the referenced object will be, you can customize the mapping, either by adding JAXB annotations to the schema or through an external binding declaration (e.g., xjc -b bindings.xjb).

Example using JAXB schema annotations (not tested):

<xs:element name="owner">
    <xs:complexType>
        <xs:attribute name="id" type="xs:IDREF">
            <xs:annotation>
                <xs:appinfo>
                    <jaxb:property>
                        <jaxb:baseType name=”SomeType”/>
                    </jaxb:property>
                </xs:appinfo>
            </xs:annotation>
        </xs:attribute>
    </xs:complexType>
</xs:element>

Example using an external bindings declaration (not tested):

<jaxb:bindings node="//xs:element[@name=’owner’]//xs:attribute[@name='id']”>
    <jaxb:property>
        <jaxb:basetype name="SomeType"/>
    </jaxb:property>
</jaxb:bindings>
markusk
  • 6,477
  • 34
  • 39
  • **markusk** has answered what I didn't explicitly know but guessed what the case. Provided you can rely on validation to enforce these constraints in your business data, I don't think using keys should be a problem, but from this I guess JAXB won't help you. Good luck! – Tom W Dec 20 '10 at 19:59
  • thank you. that looks just like the stuff im doing now :) It works OK, but to have even an illusion of type safety you have to manually change the references in the generated owner classes from Object to the real reference class. And theres no way to have real type safety. This kinda sucks, i really would like to enforce it and thought it would be possible by means of key/keyref – kostja Dec 21 '10 at 06:41
  • @kostja: You can customize the JAXB mapping to specify the reference class. I have updated my answer with (untested) samples of how to do this. – markusk Dec 22 '10 at 12:00