1

我想通过一个丑陋的黑客来解决这个问题:向我的“任何 XML”声明一个“错误的 DTD”......用一个例子来解释:

输入(任何 XML 片段)

<root id="root">
  <p id="p1"><i>Title</i></p>
  <p id="p2"><b id="b1">AAA<sup>1</sup>, BBB<sup>2</sup></b></p>
</root>

PHP代码,

   $DTD = '
   <!DOCTYPE noname [
   <!ATTLIST ANY
      id     ID             #IMPLIED
   >
   ]>';

   $dom = new DomDocument();
   $dom->loadXML( "$DTD\n$input" );
   $e = $dom->getElementById('p1');

   var_dump($e);

这段代码不是一个解决方案:$e 是 NULL,我不明白为什么......所以,问题是:是否可以表达一个解决这个问题的“最小 DTD”?

4

1 回答 1

1

如果您只想让 ID 机制工作,最简单的选择是使用xml:id

<root xml:id="root">
  <p xml:id="p1"><i>Title</i></p>
  <p xml:id="p2"><b xml:id="b1">AAA<sup>1</sup>, BBB<sup>2</sup></b></p>
</root>

根据https://fosswiki.liip.ch/display/BLOG/GetElementById+Pitfallsxml:id应该可以getElementById在 PHP 中使用。


您尝试的问题:

  1. 后面的元素名称<!DOCTYPE必须与 XML 文档的根元素的名称相匹配。在您的情况下,noname!=root不起作用。请参阅http://www.w3.org/TR/xml/#sec-prolog-dtd

  2. 必须为每个元素声明属性。您不能为 声明属性ANY。而且即使一个元素的内容模型是ANY,你仍然必须声明所有可能出现的元素。

所以没有办法为ID解析创建一个DTD。以下验证,它不能真正小于这个:

<!DOCTYPE root [
<!ELEMENT root (p+)>
<!ATTLIST root
         id  ID   #IMPLIED>
<!ELEMENT p ANY>
<!ATTLIST p
         id  ID   #IMPLIED>
<!ELEMENT b ANY>
<!ATTLIST b
         id  ID   #IMPLIED>
<!ELEMENT sup (#PCDATA)>
<!ELEMENT i (#PCDATA)>
]>
<root id="root">
  <p id="p1"><i>Title</i></p>
  <p id="p2"><b id="b1">AAA<sup>1</sup>, BBB<sup>2</sup></b></p>
</root>

只要 XML 解析器不尝试验证,就可以提供更小的 DTD。xmllint(和 PHP)以非验证模式接受此文档:

<!DOCTYPE anyname [ 
<!ATTLIST p id ID #IMPLIED> 
]>
<root id="root">
  <p id="p1"><i>Title</i></p>
  <p id="p2"><b id="b1">AAA<sup>1</sup>, BBB<sup id="b1">2</sup></b></p>
</root>

p并报告元素的ID 唯一性违规。

如果 xmllint 使用该--postvalid选项运行(或 PHP 在LIBXML_DTDVALID启用的情况下运行),则会发出:

test.xml:4: element root: validity error : root and DTD name do not match 'root' and 'anyname'
于 2013-10-01T17:34:02.750 回答