DB2 UTF-8 XML C2 85 到换行转换

DB2 UTF-8 XML C2 85 to new line conversion

我们在 table 中保存编码为 DB2 9.7 LUW 的 XML 数据 ( UTF-8) 时遇到问题。

Table DDL:

CREATE TABLE DB2ADMIN.TABLE_FOR_XML
(   
  ID           INTEGER NOT NULL, 
  XML_FIELD      XML   NOT NULL
)

问题出现在一些罕见的 Unicode 字符的例子中,我们使用 java jdbc db2 driver。

例如在正常模式下查看编辑器而不是十六进制视图 (Notepad++) 下面这个奇怪的 A(在 16. 之后)在黑色方块中表示为 NEL

输入 XML 采用 UTF-8 编码,在 HEX 编辑器中查看时具有以下值:

00000010h: 31 36 2E 20 C2 85 42                            ; 16. Â…B

After inserting in DB2 I presume that some kind of conversion occurs because when selecting data back this same character are now

00000010h: 31 36 2E 20 0D 0A 42                            ; 16. ..B

C2 85 转换为 0D 0A 即换行。

另一件事我注意到,虽然将 XML 保存到 table header 内容开始于 <xml version="1.0" encoding="UTF-8">

但是从 db2 中获取 xml 后内容开始于

<xml version="1.0" encoding="UTF-16">

有没有办法强制 db2 在不进行转换的情况下以 UTF-8 格式存储 XML?使用 XMLSERIALIZE 获取没有帮助

SELECT XML_FIELD AS CONTENT1, XMLSERIALIZE(XML_FIELD as cLOB(1M)) AS CONTENT2 from DB2ADMIN.TABLE_FOR_XML

在 content2 中没有 XML header 但有 stile newLine。

此行为是 XML 1.1 处理器的标准行为。 XML 1.1 s2.11:

the XML processor must behave as if it normalized all line breaks in external parsed entities (including the document entity) on input, before parsing, by translating [the single character #x85] to a single #xA character

行尾类型是文档的众多细节之一,这些细节将在解析和序列化循环中丢失(例如属性顺序、标签中的空格、数字字符引用...)。

DB2 的 XML 字段使用 XML 1.1 有点令人惊讶,因为很少有人使用 XML 的修订版,但对 NEL(古老的,无用的大型机行结束字符)是 IBM 唯一想要的东西。

Is there way to force db2 to store XML in UTF-8 without conversions ?

使用 BLOB?

如果您需要原生XML字段功能来保留文档的原始序列化格式,那么您将需要两列。

(你确定你需要保留 NEL 行尾吗?通常没有人关心行尾,这些都是假的。)

因为我通常不需要不可读字符,所以在将 XML 字符串保存到 Db2 之前,我决定从 x'c285(代码点 133)和 4 字节 UTF-8 字符实现干净的字符串,仅用于案例:

找到类似的例子(How to replace/remove 4(+)-byte characters from a UTF-8 string in Java?)并进行了调整。

public static final String LAST_3_BYTE_UTF_CHAR = "\uFFFF";
public static final String REPLACEMENT_CHAR = "\uFFFD"; 

public static String toValid3ByteUTF8String(String line)  {
    final int length = line.length();
    StringBuilder b = new StringBuilder(length);
    for (int offset = 0; offset < length; ) {
       final int codepoint = line.codePointAt(offset);

       // do something with the codepoint
       if (codepoint > LAST_3_BYTE_UTF_CHAR.codePointAt(0)) { //4-byte UTF replace
           b.append(REPLACEMENT_CHAR);
       } else if( codepoint == 133){ //NEL or x'c285 
           b.append(REPLACEMENT_CHAR);
       } else {
           if (Character.isValidCodePoint(codepoint)) {
               b.appendCodePoint(codepoint);
           } else {
               b.append(REPLACEMENT_CHAR);
           }
       }
       offset += Character.charCount(codepoint);
    }
    return b.toString();
}