我正在尝试确定当XML解析器在下面的示例中读取元素a
的属性x
时会发生什么:
<!DOCTYPE x [
<!ELEMENT x EMPTY>
<!ATTLIST x a CDATA #IMPLIED>
<!ENTITY d "
">
<!ENTITY a "
">
<!ENTITY t "	">
<!ENTITY t2 " "><!-- a real tab-->
]>
<x a="CARRIAGE_RETURNS:(&d;
),NEWLINES:(&a;
),TABS:(&t;	&t2; )"/><!-- a real tab at the end -->
规范中Attribute-Value Normalization规则的基本部分涉及遍历属性值并应用此case语句:
我对这些规则的阅读将使我认为属性值的XML解析器的输出应如下所示(解释:无论是在属性还是实体中,相同的规则都适用 - 保留字符引用,替换实际字符):
CARRIAGE_RETURNS:([CR] [CR]),换行:([NL] [NL]),TABS:([TAB] [TAB] [SPACE] [SPACE])
然而,在规范中略低于示例的示例表明输出应该如下,并且我编写的Java测试以这种方式工作(解释:如果它是实体值,它总是 替换):
CARRIAGE_RETURNS:([SPACE] [CR]),换行:([SPACE] [NL]),TABS:([SPACE] [TAB] [SPACE] [SPACE])
另一方面,我在PHP中编写的测试输出了这个(解释:如果它是一个实体值,那么从不替换它):
CARRIAGE_RETURNS:([CR] [CR]),换行:([NL] [NL]),TABS:([TAB] [TAB] [TAB] [SPACE])
使用xsltproc工具通过标识XSLT转换运行xml文件,给出了类似的输出:
<x a="CARRIAGE_RETURNS:( ),NEWLINES:( ),TABS:(			 )"/>
所以我的问题是:应该发生什么以及为什么?
以下示例PHP和Java程序:
PHP:
// Library versions from phpinfo():
// DOM/XML API Version 20031129
// libxml Version 2.6.32
$doc = new DOMDocument();
$doc->load("t.xml");
echo str_replace(array("\t", " ", "\r", "\n"), array("[TAB]", "[SPACE]", "[CR]", "[NL]"), $doc->documentElement->getAttribute("a")), "\n";
爪哇:
import java.io.*;
class T{
public static void main(String[] args) throws Exception {
String xmlString = readFile(args[0]);
System.out.println(xmlString);
org.w3c.dom.Document doc =
javax.xml.parsers.DocumentBuilderFactory.newInstance().
newDocumentBuilder().
parse(new org.xml.sax.InputSource(new StringReader(xmlString)));
System.out.println(doc.getImplementation());
System.out.println(
doc.
getDocumentElement().
getAttribute("a").
replace("\t", "[TAB]").
replace(" ", "[SPACE]").
replace("\r", "[CR]").
replace("\n", "[NL]")
);
}
// Very rough, but works in this case
private static String readFile(String fileName) throws IOException {
File file = new File(fileName);
InputStream inputStream = new FileInputStream(file);
byte[] buffer = new byte[(int)file.length()];
int length = inputStream.read(buffer);
String result = new String(buffer, 0, length);
inputStream.close();
return result;
}
}
答案 0 :(得分:1)
所以问题是,实体的替换文本是一个回车字符,还是表示回车字符的字符实体?
如果你看一下XML建议书附录D中的例子(特别是描述为“更复杂的例子”),看来替换文本(在你的例子中)应该是一个回车字符,而不是角色实体。这意味着您的“Java测试”是正确的。至少,如果我对附录的解释是正确的。
但请注意,附录D是非规范性的,这意味着您必须阅读建议书正文以找出实际规则。我相信这是第4.4节,但那张桌子让我头疼。
答案 1 :(得分:1)
Section 4.5: Construction of Entity Replacement Text定义了两个重要的区别。
对于我们当前的目的,外部实体可以被认为是C或PHP中的包含文件 - 它是一个文件或其他外部资源,其内容被插入然后被处理。内部实体在DTD的有效负载中携带,并且为了确保可以携带任意内部实体而不与DTD语法混淆,它以转义形式承载,称为文字实体值 。为了将文字实体值转换为替换文本,将应用以下规则:
对于内部实体, 替换文本是内容 更换字符后的实体 引用和参数实体 引用。
所以:
"[TAB]"
的文字实体值映射到替换文字[TAB]
。我在这里声明了一个特殊的转义机制,其中[TAB]表示制表符,因为我无法在此文本框中键入制表符并让它理解 - 我希望这不会混淆事物,而是展示事实有充分的理由有逃生机制,所以重要的是要了解它们的使用位置以及看起来复杂的东西如何分解成不同级别的逃逸机制。"&x9;"
的文字实体值也会映射到替换文字[TAB]
。因此,就属性值规范化逻辑而言,它是一个选项卡,并且它不知道它是使用字符引用在内部实体中表示的。看起来这似乎是多余的,或者某些信息丢失了,但实际上并没有 - 转义机制允许你逃避任何事情,包括你不需要逃避的事情 - 例如你可能会取代{{3}的每一次使用在a
的HTML文件中,既没有获取也没有丢失信息。"&#x9;"
的文字实体值映射到替换文字	
。属性值规范化逻辑将其解释为选项卡的字符引用,并将其值标准化为选项卡而不是将其折叠。 "&#38;#x9;"
的文字实体值映射到替换文字&#x9;
似乎是某种逐个或双重编码错误,为了使[TAB]显示在属性值中,您的内部实体必须包含文字文本&#x9;
。由于DTD碰巧使用与XML相同的字符转义机制,但出于不同的原因,会产生双重编码错误的印象。如果DTD使用了不同的转义机制,例如沿\u0009
行的选项卡,那么文字实体值将包含散布着&amp;#xyyyy-escaped字符的\ uyyyy-escaped字符,我们总能说出什么逃生机制属于什么水平。无论如何,这不是它的完成方式,所以我们必须对发生的事情有一个很好的了解...例如,如果你正在编写一个正则表达式来检测反斜杠,你必须通过正则表达式中的反斜杠来逃避加倍它,如果你使用没有正则表达式文字的语言,你必须将它放在一个带有正确转义的字符串中,所以它最终会成为一行中的四个反斜杠,这看起来完全错误但是当你想到它时它是正确的不同级别的转义机制的交互(顺便说一下,我原本试图写出那些反斜杠,但为了绕过Stackoverflow自己的转义机制,我不得不连续写出8个反斜杠,而且感觉不安全写那个)
上面对我来说似乎不错,作为示例代码中演示的规范和Java实现的解释。它显然与PHP示例不一致,我并不是说暗示存在错误 - PHP DOM实现位于成熟的C库之上,具有许多配置选项,其中一个或多个可能是可调整的获得与Java示例一致的行为。像这样的例子给我带来了XML的复杂性......简单的解释就像我上面提到的那样可能对95%的时间内得到的概念有用,但其他5%可能很难理解和解释。因此,如果我的解释存在缺陷,或者您有更好的解释,请添加评论或其他答案,越迂腐越好。