XSD:元素和属性之间的选择

时间:2014-04-02 13:07:34

标签: xml jaxb xsd

我想要一个包含内容或某个属性但不同时包含两者的元素。 元素可以如下所示:

<Location ref="blah"/>

或者这个:

<Location> <aaaLocation>...</aaaLocation> <Location>

但不是这样的:

 <Location ref="blah"> <aaaLocation>...</aaaLocation> <Location>

我尝试了一些变体:

<xs:complexType name="FatherOfLocatiion">
        <xs:choice>
            <xs:element name="Location">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name ="aaaLocation" type="Alocation"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="Location">
                <xs:complexType>
                    <xs:attribute name="ref" type="xs:string" />
                </xs:complexType>
            </xs:element>
        </xs:choice>
        <xs:attribute name="name" type="xs:string" use="required"/>
    </xs:complexType>

模式在XML spy等工具中有效,但当我尝试使用jaxb从中生成对象时,我收到以下错误:

模型组中会出现名称为“位置”且名称不同的多个元素。

还有其他方法可以强制执行吗?

3 个答案:

答案 0 :(得分:4)

有一种方法可以在XSD 1.0中实现,它涉及使用xsi:type属性; xsi:type是一个XML属性,但是,支持XSD的XML处理器会根据您的需要对它进行不同的处理。 (为了完整起见,这种方法是XSD 1.1中替代类型的前身,另一种实现您想要的方式。)

鉴于以下XSD 1.0:

<?xml version="1.0" encoding="utf-8" ?>
<!-- XML Schema generated by QTAssistant/XSD Module (http://www.paschidev.com) -->
<xsd:schema elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="Location"/>
    <xsd:complexType name="LocationWithRef">
        <xsd:attribute name="ref" type="xsd:string" use="required"/>
    </xsd:complexType>
    <xsd:complexType name="LocationWithContent">
        <xsd:sequence>
            <xsd:element name="a"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema> 

以下XML有效,但没有其他组合;属性:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<Location xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LocationWithRef" ref="a"/>

使用元素:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<Location xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LocationWithContent"><a/></Location>

以上内容足以模拟您为XML世界指明的内容;但是,对于XSD代码绑定技术,你需要采取额外的步骤使其友好,就像JAXB(或.NET)。问题是将它们与抽象基类型(下面是ALocation)链接起来。

<?xml version="1.0" encoding="utf-8" ?>
<!-- XML Schema generated by QTAssistant/XSD Module (http://www.paschidev.com) -->
<xsd:schema elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="Location" type="ALocation"/>
    <xsd:complexType name="ALocation" abstract="true"/>
    <xsd:complexType name="LocationWithRef">
        <xsd:complexContent>
            <xsd:extension base="ALocation">
                <xsd:attribute name="ref" type="xsd:string" use="required"/>                
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
    <xsd:complexType name="LocationWithContent">
        <xsd:complexContent>
            <xsd:extension base="ALocation">
                <xsd:sequence>
                    <xsd:element name="a"/>
                </xsd:sequence>             
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:schema> 

上述优势在于它提供了你所问的内容(唉,xsi:类型可能不适合你或其他人,喜欢)基于几乎所有XSD处理器都支持的技术那里。

不幸的是,XSD 1.1的支持是非常有限的,如果我不得不猜测,它需要一段时间才能得到JAXB的支持(你似乎对它感兴趣),更不用说其他XSD代码绑定了解决方案...

答案 1 :(得分:1)

使用XSD 1.0无法实现此目的,但您可以使用 XSD 1.1

要告诉您的解析器您的XSD是1.1,请将这些属性添加到xs:schema元素:

xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1"

如果您的解析器支持XSD 1.1,它将理解断言,您可以使用XPath来表达您的规则。

在此XSD文档中:

<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:element name="FatherOfLocation" type="FatherOfLocation"/>
    <xs:complexType name="FatherOfLocation">
        <xs:sequence>
            <xs:element name="Location" maxOccurs="unbounded">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name ="aaaLocation" type="xs:string" minOccurs="0"/>
                    </xs:sequence>
                    <xs:attribute name="ref" type="xs:string" use="optional"/>
                    <xs:assert test="(@ref and not(aaaLocation)) or (not(@ref) and aaaLocation)"></xs:assert>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

ref属性和aaaLocation元素都是可选的,但assert声明了一个限制它的规则(在complexType的上下文中使用XPath):

(@ref and not(aaaLocation)) or (not(@ref) and aaaLocation)

因此它将验证:

<Location ref="blah"/>

<Location> <aaaLocation>...</aaaLocation> </Location>

验证

<Location ref="blah"> <aaaLocation>...</aaaLocation> </Location>

<Location></Location>

答案 2 :(得分:0)

唉,您希望根据其中一个属性限制元素的内容。这可以使用其他模式语言(如Relax NG),但不能使用W3C XML Schema。奇怪的是XML Spy没有引起任何错误。

通常的解决方法是使用2个不同的元素名称,例如LocationLocationWithRef