【发布时间】:2017-04-11 23:27:43
【问题描述】:
我正在编写一个 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 map="" display_name=""> 中放置 <zone.display_name> 元素不会导致架构失败,而反转 <zone display_name="" map=""> 等属性会触发失败。
如果我理解清楚的话,问题似乎是通配符 @* 实际上没有用作 concat(name(), '.', name(@*)) 中的列表,因为 concat() 实际上需要一个字符串,和 name() 单个元素,如 this answer 中所述。
那么我如何才能真正检查每个属性,在子元素中没有等效元素?
这是一个嵌套循环,可以用伪代码表示为:
for attribute in element.attributes:
for child in element.children:
if child.name == element.name + "." + attribute.name:
raise Error
有什么想法吗?我觉得我很近!
【问题讨论】:
标签: xml xpath schematron