XML Schema key/keyref - how to use them?

2019-01-11 09:55发布

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

5条回答
SAY GOODBYE
2楼-- · 2019-01-11 10:27

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

查看更多
看我几分像从前
3楼-- · 2019-01-11 10:29

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.

查看更多
放荡不羁爱自由
4楼-- · 2019-01-11 10:49

Please, try to see this key/keyref tutorial.

The colored text is very helpful.

查看更多
淡お忘
5楼-- · 2019-01-11 10:50

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:

查看更多
smile是对你的礼貌
6楼-- · 2019-01-11 10:50

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>
查看更多
登录 后发表回答