如何强制 XML 文件中存在某个元素?

How to enforce the presence of a certain element in the XML file?

我想强制 <a-special/> 元素在我的文档中 至少出现一次 。对于这样的语法,这样的文档将是有效的(因为 <a-special/> 出现):

<my-container>
    text <a id="1" type="B"/> text text <a-special/>
    text text <a id="5" type="B"/> text <a id="24" type="B"/>
    text <a id="5" type="C"/>
</my-container>

而这将被视为无效(因为 <a-special/> 不会 发生):

<my-container>
    <a id="1" type="B"/> text text
    text <a id="5" type="B"/> text <a id="24" type="B"/>
    text <a id="5" type="C"/>
</my-container>

我已经用下面的语法尝试了不同的东西,但我似乎无法让它按照我需要的方式工作。

<!ELEMENT my-container ( #PCDATA | a | a-special | b )*>

<!ELEMENT a-special EMPTY>

<!ELEMENT a EMPTY>
    <!ATTLIST a id CDATA #REQUIRED>
    <!ATTLIST a type CDATA #REQUIRED>

<!ELEMENT b EMPTY>
    <!ATTLIST b id CDATA #REQUIRED> 
    <!ATTLIST a type CDATA #REQUIRED>

我知道这是错误的,但我在想这样的事情:

<!ELEMENT my-container 
              a-special+ ( #PCDATA | a | b | a-special )*                           
            | ( #PCDATA | a | b )+ a-special+ ( #PCDATA | a | b | a-special )*
            >

第一部分将解析任何以 a-special 开头的内容,第二部分将解析任何需要 a-special 介于两者之间或末尾的内容。这可以用 DTD 语法来完成吗?

您要强制执行的约束不能用 XML DTD 声明。

如果您的最外层元素真的只是一系列字符数据和空子元素,您提到的类似内容模型的表达式将(在提供缺少的逗号之后)准确地捕获约束:

((#PCDATA | a | b)*, a-special, (#PCDATA | a | b | a-special)*)

这在 SGML 中是合法的(我认为是这样,但我还没有检查过)。但是 XML DTD 中混合内容唯一允许的形式是

(#PCDATA)
(#PCDATA | x | y | ... |z)*
(#PCDATA)*

所描述的约束可以在 XSD 或 Relax NG 中表达。

如果允许文档元素以外的任何元素为非空,则约束不能用我所知道的任何模式语言的内容模型表达:内容模型作为一种上下文无关语法,并且文档中 某处 有一个 a-special 元素的要求需要一种上下文敏感的形式。

正如@potame 在评论中观察到的那样,Schematron 可以制定约束; XSD 1.1 中的断言也可以附加到文档元素的声明中。

一种可能的解决方法:以不同的方式标记元素的特殊性,例如通过指向文档中的某些 a 个元素:

<!ELEMENT my-container (#PCDATA|a|b)* >
<!ATTLIST my-container specials IDREFS #REQUIRED >
<!ELEMENT a EMPTY >
<!ATTLIST a id ID #IMPLIED>
<!ELEMENT b EMPTY>

由于my-container/@specials是必需的,它必须至少命名文档中的一个元素。由于为其定义 ID 的唯一元素类型是 a,因此由 specials 命名的元素保证是 a 个元素。

如果您使用 XSD 而不是 DTD,您可以只在元素中使用 minOccurs 属性。