从UnicodeString创建CData节点时出现XML无效字符问题

3

IDE: Embarcadero XE5 C++ Builder。

我正在尝试将 UnicodeStrings 转储到 XML CData 部分中。

以下是部分字符串:

 u"‰PNG\r\n\x1A\n\0\0\0\rIHDR\0\0\0õ\0\0\02\b\x06\0\0\0„\\i\0\0\0\x01sRGB\0®Î\x1Cé\0\0\0\x04gAMA\0\0±\vüa\x05\0\0\0\tpHYs\0\0\x0EÃ\0\0\x0EÃ\x01Ço¨d\0\0\v¼IDATxÚíœypUÕ\x19ÀO\x06…°¤\x04D$ˆ²\b1š\b\x18@...etc"

我知道XML文档可以包含非ASCII字符,而且我认为XML CData部分的内容不会被XML解析器解析(除了结束部分指示符“[[>”,它并没有出现在我的数据中,我已经检查过了)。当我创建(写入)CData部分时,仍然会出现“在创建节点时找到文本内容中的无效字符”错误。
代码示例:
_di_IXMLDocument pXMLDocument = NewXMLDocument("1.0");
// I've played around with the document encoding with no success, guessing it's only applicable while reading the document.
// pXMLDocument->SetEncoding(L"iso-8859-1"); 

String myString;   // Unicode, contains my data string.

// 1st param of CreateNode method is of type UnicodeString.
di_IXMLNode pCDataNode = pXMLDocument->CreateNode( myString, ntCData ); 

你认为为什么会失败?是编码问题吗?


似乎问题实际上可能是字符串内容。由于数据的“随机”性质,字符串包含大量字符字面量(转义字符)...它们被错误地(根据我的要求)解释。这会导致问题。因此,在创建CData节点之前,数据字符串需要解析转义字符。 - HvS
3个回答

10

如果您阅读XML规范第2.7节,它描述了CDATA部分的格式:

CDATA Sections

[18]    CDSect    ::=    CDStart CData CDEnd  
[19]    CDStart    ::=    '<![CDATA[' 
[20]    CData    ::=    (Char* - (Char* ']]>' Char*))  
[21]    CDEnd    ::=    ']]>' 

Char第2.2节中有定义:

Char    ::=    #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */ 

如果您查看原始数据,它包含超过十几个在该范围之外的字符值(具体来说是#x0#x1#x2#x4#x5#x6#x8#xB #xE#x18#x19#x1A#x1C)。这就是为什么会出现非法字符错误的原因,因为您确实有非法字符。

CDATA部分不允许您将任意二进制数据放入XML数据中。CDATA部分的用途是在文本内容包含通常保留给XML标记的字符时使用,这样它们就不必被转义或编码为实体。将二进制数据放入XML文档的唯一方法是将其编码为XML兼容(通常是7位ASCII)格式,例如Base64(但还有其他可用的格式,例如yEnc)。


0

针对我的情况,我创建了一个函数来修剪字符串,仅保留有效的XML字符集。

伪代码

//Code released into public domain. No attribution required.
function TrimToXmlText(xmlText: String): string;
begin
   /*
      http://www.w3.org/TR/xml/#NT-Char

      Regarless of entity encoding, the only valid characters allowed are:

         Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]

      I.e. any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
      This means that a string such as

         "Line one"#31#10"Line two"

      is invalid (because of the #31 aka 0x1F).

      This means we need to manually strip them out; because the xml library certainly won't do it for us.
   */

   SetLength(Result, Length(xmlText));

   Int32 o = 0;
   for i = 1 to Length(s) do
   begin
      case Ord(s[i]) of
      $9, $A, $D,
      $20..$D7FF,
      $E000..$FFFD:
         begin
            o = o+1;
            Result[o] = xmlText[i];
         end;
      end;
   end;

   SetLength(Result, o);
end;

0
原来问题确实是原始数据字符串中存在的所有转义字符,正如我们所怀疑的那样。
通过对整个字符串进行Base64编码,然后再创建XML CData-sections来解决这个问题。
Rad Studio方法:EncodeBase64DecodeBase64 头文件:Soap.EncdDecd.hpp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接