【问题标题】:Named entities in encapsulated XML cause parsing errors封装 XML 中的命名实体导致解析错误
【发布时间】:2014-08-26 11:01:18
【问题描述】:

我的 XML 文档包含封装为 CDATA 的其他 XML 文档,如下所示:

    <mds>
      <md>
        <value>
          <![CDATA[<?xml version="1.0" encoding="UTF-8"?><record xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/">
             <dc:title>some text containing &amp;</dc:title></record>]]>
        </value>
      </md>
    </mds>

我使用 LibXML 从中提取这个 XML 和 dc:title:

$dcrawData = <get the CDATA from above>;
$dcDOM = $::PRSR->load_xml(expand_entities => 0, string => $dcRawData);
$dcTitle = $dcDOM->findvalue("//dc:title");

然后我通过字符串替换将它插入到另一个 XML 部分:

<mods:titleInfo>
    <mods:title>some text containing &</mods:title>
</mods:titleInfo>

如您所见,&amp 实体被扩展并成为单个 &。这是一个问题,因为现在生成的 XML 会生成一个解析错误,因为任何解析器都希望这里有一个命名实体。

有没有办法防止 LibXML 在使用 findvalue 时扩展命名实体或在使用该值之前重新编码它们?其他记录中可能还有其他人。 expand_entities 选项没有区别。

【问题讨论】:

    标签: xml perl named-entity-recognition


    【解决方案1】:

    好的,我想我找到了解决方案。 XML::Entities 将重新编码字符串中的实体。

    但是,我需要将编码的字符限制为只有几个,否则编码的字符串将包含 xml 解析器无法识别的实体。

    所以我现在使用

    $dcTitle = encode_entities($dcDOM->findvalue("//dc:title"),'&<>"');
    

    只对 & 符号和一些特殊的 xml 字符进行编码。

    【讨论】:

    • 鉴于您正在使用的值不包含任何元素标签,转义 &amp;&lt; 应该足以保证格式正确的输出(如果您保存结果文件以正确的编码)。
    • 谢谢伊恩,我会考虑的。我不太了解数据,但看起来只会有各种语言(utf-8)的文本和一些不幸的东西,比如&符号。
    • findvalue 永远不会给你元素 - 无论 XPath 表达式返回什么,它都会给你to_literal,所以在这种情况下,它会给你一个包含字符串值串联的字符串(即您正在查看的文档中所有 dc:title 元素的文本内容。
    • @Ian:你是对的。在我项目的其他地方,我确实使用了 findnodes-route 以避免将多个元素加入到一个 findvalue 中。但是在这种情况下,将只有一个标题,无需担心。
    【解决方案2】:

    然后我通过字符串替换将它插入到另一个 XML 部分中

    不要。如果您想将数据插入 XML 文档,那么您应该使用 XML 感知 API,该 API 将为您处理任何必要的转义。

    【讨论】:

    • 是的,我意识到这一点。但这意味着要为数据迁移项目丢弃一个非常非常复杂的脚本并从头开始重建所有内容,从而引入大量其他问题,这些问题伴随着使用 DOM 构建复杂的 XML ......命名空间和前缀处理,元素 id文档中引用的只是立即想到的内容。你当然是对的,这是正确的做法。
    • @jackthehipster 很公平,考虑到更大的背景,我可能也会这样做。
    【解决方案3】:

    然后我通过执行字符串替换将它插入到另一个 XML 部分中

    这是你做错的部分。您将文本插入 XML 而不将其转换为 XML。 (这称为注入错误。)您需要转义 &amp;&lt; 以及文档字符集之外的任何字符。

    sub text_to_xml {
       my ($s) = @_;
       for ($s) {
          s/&/&amp;/g;
          s/</&lt;/g;
          s/"/&quot;/g;  # So it can be used for attributes too
          s/'/&apos;/g;  # So it can be used for attributes too
       }
       return $s;
    }
    

    不要忘记,您还需要根据文档的编码对其进行编码。

    【讨论】:

    • 见我上面的回答。我现在正在使用 XML::Entities::encode_entities() 做同样的事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-17
    • 2012-04-11
    • 1970-01-01
    • 2011-04-17
    相关资源
    最近更新 更多