如何在xpath中选择属于同一组的节点?

时间:2011-10-18 23:17:47

标签: xml select xpath schema

我有一个XML文档,它对XML模式有效。 XML模式具有组元素(xs:group)。这些组由其他定义的元素组成。如何编写一个XPath表达式,它将为我提供指定组的所有成员?

有什么想法吗?

@Steve:

假设我的xml架构定义了4个元素( elem1,elem2,elem3,elem4 )。另外,2组定义如下:

group1: (elem1 | elem2 | elem3)
group2: (elem1 | elem4)

我希望你知道一些正则表达式。如果不是,那么'group2:(elem1 | elem4)'只是意味着group2由一个elem1或一个elem4组成。

我的问题是我是否有一个xml文档,如:

<elem1/>
<elem2/>
<elem3/>
<elem4/>
<elem2/>
<elem1/>
<elem3/>

如何列出该文档中属于group1

的元素
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> 
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="elem0"/>
                <xs:choice minOccurs="0" maxOccurs="unbounded">
                    <xs:group ref="A1"/>
                    <xs:group ref="A2"/>
                </xs:choice>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="elem0" type="xs:string"/>

    <xs:group name="A1">
        <xs:choice>
            <xs:element ref="elem10"/>
            <xs:element ref="elem11"/>
        </xs:choice>
    </xs:group>

    <xs:element name="elem10" type="xs:string"/>
    <xs:element name="elem11" type="xs:string"/>

    <xs:group name="A2">
        <xs:choice>
            <xs:element ref="elem20"/>
            <xs:element ref="elem21"/>
            <xs:element ref="elem22"/>
            <xs:element ref="elem23"/>
        </xs:choice>
    </xs:group>

    <xs:group name="CE">
        <xs:choice>
            <xs:element ref="elem30"/>
            <xs:element ref="elem31"/>
            <xs:element ref="elem32"/>
        </xs:choice>
    </xs:group>

    <xs:group name="E">
        <xs:choice>
            <xs:element ref="elem30"/>
            <xs:element ref="elem40"/>
        </xs:choice>
    </xs:group>

    <xs:element name="elem20">
        <xs:complexType>
            <xs:sequence>
                <xs:group minOccurs="2" maxOccurs="unbounded" ref="CE"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="elem21">
        <xs:complexType>
            <xs:sequence>
                <xs:group minOccurs="2" maxOccurs="2" ref="CE"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="elem22">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="elem40"/>
                <xs:group ref="CE"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="elem23">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="elem40"/>
                <xs:element ref="elem40"/>
            </xs:sequence>
            <!-- <xs:attribute name="prop" use="required" type="xs:NMTOKEN"/> -->
        </xs:complexType>
    </xs:element>

    <xs:element name="elem31">
        <xs:complexType>
            <xs:sequence>
                <xs:group minOccurs="0" maxOccurs="unbounded" ref="CE"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="elem32">
        <xs:complexType>
            <xs:sequence>
                <xs:group ref="CE"/>
            </xs:sequence>
            <!-- <xs:attribute name="prop" use="required"/> -->
        </xs:complexType>
    </xs:element>

    <xs:element name="elem30">
        <xs:complexType>
            <xs:attribute name="name" use="required"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="elem40">
        <xs:complexType>
            <xs:attribute name="name" use="required"/>
        </xs:complexType>
    </xs:element>
</xs:schema>

1 个答案:

答案 0 :(得分:2)

好的......我认为我们需要在你的例子中澄清一些事情 - 因为,虽然它们看起来像小点,但实际上它们不是 - 而且,如果你遵守规则,它应该是直截了当的如何构造XPath表达式(我将展示如何构建基本XPath表达式的示例,将组考虑为有效模式,然后我的示例问题是什么)。

让我们分一步。

具有序列的组

首先,我们假设您有一个如下所示的架构:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:group ref="group1"/>
                <xs:group ref="group2"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:group name="group1">
        <xs:sequence>
            <xs:element name="elem1"/>
            <xs:element name="elem2"/>
            <xs:element name="elem3"/>
        </xs:sequence>
    </xs:group>
    <xs:group name="group2">
        <xs:sequence>
            <xs:element name="elem1"/>
            <xs:element name="elem2"/>
        </xs:sequence>
    </xs:group>
</xs:schema>

在这种情况下,需要注意的重要一点是,我们有一个序列 group1后跟group2这两个序列元素。

使用序列,(并且minoccurs='0'元素上没有group属性 - 无论如何我将在稍后解释,这将是无效的),选择所需的元素是微不足道的。

要选择group1的所有元素,我们可能只使用以下XPath:

/root/(elem1[1]|elem2[1]|elem3)

这很有效,因为我们知道生成的XML将始终为:

<root>
     <elem1 />
     <elem2 />
     <elem3 />
     <elem1 />
     <elem2 />
</root>

所以,没关系。我们可以始终选择第一个elem1,第一个elem2elem3

有选择的群组

让我们假设,而不是那些包含序列的组,而是包含选项。模式如下所示:

(这更类似于您在示例中放置的架构,其中“group2由一个elem1或一个elem4组成。”)

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:group ref="group1"/>
                <xs:group ref="group2"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:group name="group1">
        <xs:choice>
            <xs:element name="elem1"/>
            <xs:element name="elem2"/>
            <xs:element name="elem3"/>
        </xs:choice>
    </xs:group>
    <xs:group name="group2">
        <xs:choice>
            <xs:element name="elem1"/>
            <xs:element name="elem2"/>
        </xs:choice>
    </xs:group>
</xs:schema>

在这种情况下,XPath仍然很容易构造,因为我们知道只有两个元素,第一个属于group1,第二个属于group2,就像这样:

<root>
     <elem2 />
     <elem1 />
</root>

所以group1 XPath更简单:

/root/*[1]

独特粒子归因

这里可能会让人感到困惑 - 而且我相信,你的困惑来自哪里。

在您的示例中,您基本上建议了以下架构:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:group ref="group1" maxOccurs="unbounded"/>
                <xs:group ref="group2" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:group name="group1">
        <xs:choice>
            <xs:element name="elem1"/>
            <xs:element name="elem2"/>
            <xs:element name="elem3"/>
        </xs:choice>
    </xs:group>
    <xs:group name="group2">
        <xs:choice>
            <xs:element name="elem1"/>
            <xs:element name="elem2"/>
        </xs:choice>
    </xs:group>
</xs:schema>

此架构无效。(请注意,在组上添加了maxOccurs="unbounded"属性)。这与您在一个组中显示多个元素的示例类似。

为什么呢?好吧,因为它在生成的XML中产生了潜在的歧义。

例如,我们应该如何解析以下XML实例:

<root>
     <elem2 />
     <elem1 />
     <elem1 />
     <elem2 />
</root>

那是:

  • group1group1group1group1
  • group1group1group1group2
  • group1group1group2group1
  • group1group2group1group1
  • ...

我们只是不知道。

但是XML Schemas的设计者对此进行了思考并为此制定了规则:

http://en.wikipedia.org/wiki/Unique_Particle_Attribution

您的假设架构违反了该规则。

现在,v1.1确实在这方面做了一些改进......但是,仍然存在可以轻松创建类似歧义的情况。

在您的示例中,如果xml中不存在元素3或4,则无法确定group1的结束位置和group2的开始位置。

现在,如果您只想选择具有特定名称的元素,则可以轻松完成:

/root/(elementName1|elementName2|elementName3)

将选择名称为rootelementName1elementName2的{​​{1}}下的所有元素。

所以,在你的例子中,像elementName3这样的东西就可以了。

但是,那不是你问的。你问的是关于按群组选择 - 而你提供的例子使你无法给出小组的真实答案。

如果你有真正的有效的架构,并且需要帮助构建XPath,请粘贴那个架构,我会很乐意提供帮助。