Xpath 通配符 returns 仅第一个元素
Xpath wildcard returns only the first element
我正在编写一个 schematron 来验证以下 xml 文件:
<root version="1.0">
<zone map="fields.map" display_name="Fields">
<zone.rectangles>
<rectangle h="2" w="2" x="0" y="0" />
</zone.rectangles>
</zone>
</root>
我想确保如果声明了任何元素的属性,则该元素不能包含与该属性同名的子元素。
例如,如果 <zone>
具有属性 map
,则 <zone>
不能包含元素 <zone.map>
。
因此,前面的xml文件有效,但下面的文件无效:
无效:
<root version="1.0">
<zone map="fields.map" display_name="Fields">
<zone.map>fields.map</zone.map>
<zone.rectangles>
<rectangle h="2" w="2" x="0" y="0" />
</zone.rectangles>
</zone>
</root>
另一方面,这个有效:
有效:
<root version="1.0">
<zone display_name="Fields">
<zone.map>fields.map</zone.map>
<zone.rectangles>
<rectangle h="2" w="2" x="0" y="0" />
</zone.rectangles>
</zone>
</root>
我使用这个 schematron 文件得到它:
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern>
<title>Attribute usage</title>
<!-- Every element that has attributes -->
<rule context="*[@*]">
<!-- The name of its children should not be {element}.{attribute} -->
<assert test="name(*) != concat(name(), '.', name(@*))">
The attribute <name />.<value-of select="name(@*)" /> is defined twice.
</assert>
</rule>
</pattern>
</schema>
经过无数次不幸的尝试,我花了大约 4 个小时才使它正常工作,所以我对这个架构非常满意,并开始对其进行更多测试。
看到它只对每个元素的第一个属性起作用,我真的很失望。例如,对于 zone
元素,仅测试 map
属性。因此,将 <zone.display_name>
元素放入 <zone map="" display_name="">
不会使架构失败,而反转 <zone display_name="" map="">
等属性将触发失败。
如果我理解得很好,问题似乎是通配符 @*
实际上没有用作 concat(name(), '.', name(@*))
中的列表,因为 concat() 实际上需要一个字符串,而 name() 是一个元素,如 .
中所述
那么我怎样才能真正检查每个属性的子元素中没有等效元素?
这是一个嵌套循环,可以用伪代码表示为:
for attribute in element.attributes:
for child in element.children:
if child.name == element.name + "." + attribute.name:
raise Error
有什么想法吗?我觉得我很近!
我终于通过使用变量让它工作了。
我使用了这个 schematron:
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern>
<title>Attribute usage</title>
<!-- Elements that contains a dot in their name -->
<rule context="*[contains(name(), '.')]">
<!-- Take the part after the dot -->
<let name="attr_name" value="substring-after(name(), '.')" />
<!-- Check that there is no parent's attributes with the same name -->
<assert test="count(../@*[name() = $attr_name]) = 0">
The attribute <name /> is defined twice.
</assert>
</rule>
</pattern>
</schema>
Schematron 非常强大,但您必须掌握它...
问题的更通用的答案:
如果您想遍历通配符 *
或 @*
,那么 count()
是您的朋友,因为它实际上考虑了元素列表。
如果您发现自己卡住了,请尝试解决问题upside-down。我先遍历属性,然后遍历 children,而现在我遍历每个元素,然后检查它们的 parent 的属性。
如果您想使用 parent 上下文中的信息,但发现自己陷入了 []
关闭,请使用变量来获取值。
例如,如果您尝试 ../@*[name() = name(..)]
,它不会执行您想要的操作,因为 []
中的 name(..)
指的是属性的 parent 名称,而不是当前上下文元素名称。
如果您将值提取为 <let name="element_name" value="name()" />
,那么您就可以开始了:../@*[name() = $element_name]
.
打开方括号后,您将无法再访问这些括号外的元素,因此请使用变量将它们放入。
编辑:
您可以使用 current()
函数从括号内获取上下文元素,而无需使用变量。我的最终架构是:
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern>
<title>Attribute usage</title>
<!-- Elements that contains a dot in their name -->
<rule context="*[contains(name(), '.')]">
<!-- Check that there is no parent's attributes with the same name -->
<assert test="not(../@*[name() = substring-after(name(current()), '.')])">
The attribute <name /> is defined twice.
</assert>
</rule>
</pattern>
</schema>
感谢 Eiríkr Útlendi 的帮助!
我正在编写一个 schematron 来验证以下 xml 文件:
<root version="1.0">
<zone map="fields.map" display_name="Fields">
<zone.rectangles>
<rectangle h="2" w="2" x="0" y="0" />
</zone.rectangles>
</zone>
</root>
我想确保如果声明了任何元素的属性,则该元素不能包含与该属性同名的子元素。
例如,如果 <zone>
具有属性 map
,则 <zone>
不能包含元素 <zone.map>
。
因此,前面的xml文件有效,但下面的文件无效:
无效:
<root version="1.0">
<zone map="fields.map" display_name="Fields">
<zone.map>fields.map</zone.map>
<zone.rectangles>
<rectangle h="2" w="2" x="0" y="0" />
</zone.rectangles>
</zone>
</root>
另一方面,这个有效:
有效:
<root version="1.0">
<zone display_name="Fields">
<zone.map>fields.map</zone.map>
<zone.rectangles>
<rectangle h="2" w="2" x="0" y="0" />
</zone.rectangles>
</zone>
</root>
我使用这个 schematron 文件得到它:
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern>
<title>Attribute usage</title>
<!-- Every element that has attributes -->
<rule context="*[@*]">
<!-- The name of its children should not be {element}.{attribute} -->
<assert test="name(*) != concat(name(), '.', name(@*))">
The attribute <name />.<value-of select="name(@*)" /> is defined twice.
</assert>
</rule>
</pattern>
</schema>
经过无数次不幸的尝试,我花了大约 4 个小时才使它正常工作,所以我对这个架构非常满意,并开始对其进行更多测试。
看到它只对每个元素的第一个属性起作用,我真的很失望。例如,对于 zone
元素,仅测试 map
属性。因此,将 <zone.display_name>
元素放入 <zone map="" display_name="">
不会使架构失败,而反转 <zone display_name="" map="">
等属性将触发失败。
如果我理解得很好,问题似乎是通配符 @*
实际上没有用作 concat(name(), '.', name(@*))
中的列表,因为 concat() 实际上需要一个字符串,而 name() 是一个元素,如
那么我怎样才能真正检查每个属性的子元素中没有等效元素?
这是一个嵌套循环,可以用伪代码表示为:
for attribute in element.attributes:
for child in element.children:
if child.name == element.name + "." + attribute.name:
raise Error
有什么想法吗?我觉得我很近!
我终于通过使用变量让它工作了。
我使用了这个 schematron:
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern>
<title>Attribute usage</title>
<!-- Elements that contains a dot in their name -->
<rule context="*[contains(name(), '.')]">
<!-- Take the part after the dot -->
<let name="attr_name" value="substring-after(name(), '.')" />
<!-- Check that there is no parent's attributes with the same name -->
<assert test="count(../@*[name() = $attr_name]) = 0">
The attribute <name /> is defined twice.
</assert>
</rule>
</pattern>
</schema>
Schematron 非常强大,但您必须掌握它...
问题的更通用的答案:
如果您想遍历通配符 *
或 @*
,那么 count()
是您的朋友,因为它实际上考虑了元素列表。
如果您发现自己卡住了,请尝试解决问题upside-down。我先遍历属性,然后遍历 children,而现在我遍历每个元素,然后检查它们的 parent 的属性。
如果您想使用 parent 上下文中的信息,但发现自己陷入了 []
关闭,请使用变量来获取值。
例如,如果您尝试 ../@*[name() = name(..)]
,它不会执行您想要的操作,因为 []
中的 name(..)
指的是属性的 parent 名称,而不是当前上下文元素名称。
如果您将值提取为 <let name="element_name" value="name()" />
,那么您就可以开始了:../@*[name() = $element_name]
.
打开方括号后,您将无法再访问这些括号外的元素,因此请使用变量将它们放入。
编辑:
您可以使用 current()
函数从括号内获取上下文元素,而无需使用变量。我的最终架构是:
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern>
<title>Attribute usage</title>
<!-- Elements that contains a dot in their name -->
<rule context="*[contains(name(), '.')]">
<!-- Check that there is no parent's attributes with the same name -->
<assert test="not(../@*[name() = substring-after(name(current()), '.')])">
The attribute <name /> is defined twice.
</assert>
</rule>
</pattern>
</schema>
感谢 Eiríkr Útlendi 的帮助!