将 varchar() 中的重音字符转换为 XML 导致 "illegal XML character"
Converting accented characters in varchar() to XML causing "illegal XML character"
我有 table 被一个应用程序写入。该字段是 varchar(max)。数据看起来像 xml。
DECLARE @poit VARCHAR(100)
SET @poit = '<?xml version="1.0" encoding="utf-8"?><test>VÍA</test>'
SELECT CONVERT(XML,@poit)
但是(似乎是因为 UTF8;删除它有效),我得到这个错误:
XML parsing: line 1, character 46, illegal xml character
有没有办法干净地转换它?
我找到了这个线程,它讨论了不支持 "non-ASCII characters" 的 varchar,但显然 I 是非 unicode。是的,我可以做到:
SELECT CONVERT(XML, REPLACE(@poit, 'encoding="utf-8"', ''))
但这是最好的方法吗?
Why does casting a UTF-8 VARCHAR column to XML require converting to NVARCHAR and encoding change?
我会尝试将您的 @poit
变量的数据类型从 VARCHAR(100)
更改为 NVARCHAR(100)
。然后将 utf-8 编码替换为 utf-16,这样您的代码将类似于:
DECLARE @poit NVARCHAR(100)
SET @poit = '<?xml version="1.0" encoding="utf-8"?><test>VÍA</test>'
SELECT CONVERT(XML,REPLACE(@poit, 'utf-8', 'utf-16'))
只要您不在 SELECT returns 大量结果中使用其中的替换调用转换,性能应该就可以了,它会完成工作.
参考:http://xml.silmaril.ie/characters.html <- 向下滚动,您会看到一些关于 utf-8 和 utf-16 之间区别的信息。希望这对您有所帮助!
如果您只想要答案而不需要完整的解释,请向下滚动到 "Conclusion"。但是,你真的应该花点时间阅读解释 😸
这里发生了一些事情:
<xml>
元素的 encoding=
属性用于表示如何解释 XML 文档的底层字节。如果字符串文字中的文档是正确的,则不需要具有 encoding
属性。如果有不正确的字符,那么 encoding
属性可以保留,因为它会通知 XML 转换这些字符原来是什么。
UTF-8 是一种 Unicode 编码,但您将变量和文字作为 VARCHAR
数据,而不是 NVARCHAR
(这还需要在字符串文字前加上大写字母-N
)。通过使用 VARCHAR
而不使用 N
前缀,如果 XML 文档中的任何字符不适合您所在的任何数据库的默认排序规则表示的代码页执行此查询时,您可能已经丢失了那些字符(即使您可以在屏幕上看到它们,它们在 VARCHAR
变量中也不正确,或者如果您做了一个简单的 SELECT
字面量).
Windows(以及 .NET、SQL 服务器等)使用 UTF-16 Little Endian。 Í
字符 Latin Capital Letter I with Acute 存在于代码页 1252 和 UTF-16LE 中作为值 205(例如 SELECT ASCII('Í'), CHAR(205);
),这就是为什么它当您删除 encoding="utf-8"
以及为什么您没有 "lose" 那个字符时,可以通过将其放置在 VARCHAR
文字和变量中来工作。但是,如该链接页面所示,UTF-8 编码中的字节序列为 195、141(是的,两个字节)。意思是,如果该字符确实是 UTF-8 编码的,那么在放入 UTF-16LE 环境中时就不会显示为该字符。
XML 转换查看该字符的字节值 205(单字节,因为它当前是 VARCHAR 数据)并尝试提供与 that[=121 等效的 UTF-16LE =] 序列是 UTF-8。除了 205 本身在 UTF-8 中不存在。因此,您需要添加下一个字符,该字符是大写字母-"A",其值为 65。虽然 UTF-8 中有两个字节序列,但其中 none 是 205、65。这就是您收到 illegal xml character
错误的原因。
由于屏幕上的文本必须是 UTF-16LE,如果源确实是 UTF-8,则必须将底层 UTF-8 字节序列转换为 UTF-16LE。 Í
的底层字节序列是 195, 141。因此我们可以通过执行以下操作从代码页 1252 的常规 ASCII 字符(因为这是当前 VARCHAR 数据)创建该序列:
DECLARE @poit VARCHAR(100);
SET @poit = '<?xml version="1.0" encoding="UTF-8"?><test>V'
+ CHAR(195) + CHAR(141) + 'A</test>';
SELECT CONVERT(XML, @poit);
Returns:
<test>VÍA</test>
数据还在VARCHAR
和encoding="utf-8"
还在<xml>
元素!
如果将数据保持为 VARCHAR
,则仅 encoding=
值的以下更改有效:
DECLARE @poit VARCHAR(100);
SET @poit = '<?xml version="1.0" encoding="Windows-1252"?><test>VÍA</test>';
SELECT CONVERT(XML, @poit);
这假定源编码确实是 "Windows-1252",它是 Latin1_General 的 Microsoft 版本,它是 Latin1_General 归类的基础。
但是,如果 "encoding" 与当前数据库默认排序规则的代码页相同,则无需指定任何 VARCHAR 数据。
最后,XML
服务器中的SQL数据是UTF-16LE,与NCHAR
和NVARCHAR
(和NTEXT
,但没有人应该再使用它了)。
结论
在将 XML 用作字符串(而不是 VARCHAR
)时使用 NVARCHAR(MAX)
数据类型。
对于没有任何改变的字符的字符串(即屏幕上的一切看起来都很完美),然后像您所做的那样简单地删除 encoding="utf-8"
。没有必要用 UTF-16
替换它,因为值的本质是 NVARCHAR
变量或文字(即以大写 - N
为前缀的字符串) .
关于使用 VARCHAR(MAX)
而不是 XML
甚至 NVARCHAR(MAX)
来保存 space,请记住 XML
数据类型在内部进行了优化,使得元素和属性名称仅在字典中存储一次,因此几乎没有 XML 的完全写出的字符串版本那么多的开销。因此,虽然 XML
类型确实将字符串存储为 UTF-16LE,但 if XML 文档有很多重复元素 and/or 属性名称,然后使用 XML
类型实际上可能比使用 VARCHAR(MAX)
:
产生更小的占用空间
DECLARE @ElementBased XML;
SET @ElementBased = (
SELECT * FROM master.sys.all_columns FOR XML PATH('Row')
);
DECLARE @AttributeBased XML;
SET @AttributeBased = (
SELECT * FROM master.sys.all_columns FOR XML RAW('Row')
);
SELECT @ElementBased AS [ElementBasedXML],
@AttributeBased AS [AttributeBasedXML],
DATALENGTH(@ElementBased) AS [ElementBasedXmlBytes],
DATALENGTH(CONVERT(VARCHAR(MAX), @ElementBased)) AS [ElementBasedVarCharBytes],
((DATALENGTH(@ElementBased) * 1.0) / DATALENGTH(CONVERT(VARCHAR(MAX), @ElementBased))
) * 100 AS [XmlElementSizeRelativeToVarcharElementSize],
DATALENGTH(@AttributeBased) AS [AttributeBasedXmlBytes],
DATALENGTH(CONVERT(VARCHAR(MAX), @AttributeBased)) AS [AttributeBasedVarCharBytes],
((DATALENGTH(@AttributeBased) * 1.0) /
DATALENGTH(CONVERT(VARCHAR(MAX), @AttributeBased))) * 100
AS [XmlAttributeSizeRelativeToVarCharAttributeSize];
Returns(至少在我的系统上):
ElementBasedXmlBytes 1717896
ElementBasedVarCharBytes 5889081
XmlElementSizeRelativeToVarcharElementSize 29.170867237180130482100
AttributeBasedXmlBytes 1544661
AttributeBasedVarCharBytes 3461864
XmlAttributeSizeRelativeToVarCharAttributeSize 44.619343798600984902900
如您所见,对于基于元素的 XML,XML
数据类型是 VARCHAR(MAX)
版本大小的 29%,对于基于属性的 XML,XML
数据类型是 VARCHAR(MAX)
版本大小的 44%。
我有 table 被一个应用程序写入。该字段是 varchar(max)。数据看起来像 xml。
DECLARE @poit VARCHAR(100)
SET @poit = '<?xml version="1.0" encoding="utf-8"?><test>VÍA</test>'
SELECT CONVERT(XML,@poit)
但是(似乎是因为 UTF8;删除它有效),我得到这个错误:
XML parsing: line 1, character 46, illegal xml character
有没有办法干净地转换它?
我找到了这个线程,它讨论了不支持 "non-ASCII characters" 的 varchar,但显然 I 是非 unicode。是的,我可以做到:
SELECT CONVERT(XML, REPLACE(@poit, 'encoding="utf-8"', ''))
但这是最好的方法吗?
Why does casting a UTF-8 VARCHAR column to XML require converting to NVARCHAR and encoding change?
我会尝试将您的 @poit
变量的数据类型从 VARCHAR(100)
更改为 NVARCHAR(100)
。然后将 utf-8 编码替换为 utf-16,这样您的代码将类似于:
DECLARE @poit NVARCHAR(100)
SET @poit = '<?xml version="1.0" encoding="utf-8"?><test>VÍA</test>'
SELECT CONVERT(XML,REPLACE(@poit, 'utf-8', 'utf-16'))
只要您不在 SELECT returns 大量结果中使用其中的替换调用转换,性能应该就可以了,它会完成工作.
参考:http://xml.silmaril.ie/characters.html <- 向下滚动,您会看到一些关于 utf-8 和 utf-16 之间区别的信息。希望这对您有所帮助!
这里发生了一些事情:
<xml>
元素的encoding=
属性用于表示如何解释 XML 文档的底层字节。如果字符串文字中的文档是正确的,则不需要具有encoding
属性。如果有不正确的字符,那么encoding
属性可以保留,因为它会通知 XML 转换这些字符原来是什么。UTF-8 是一种 Unicode 编码,但您将变量和文字作为
VARCHAR
数据,而不是NVARCHAR
(这还需要在字符串文字前加上大写字母-N
)。通过使用VARCHAR
而不使用N
前缀,如果 XML 文档中的任何字符不适合您所在的任何数据库的默认排序规则表示的代码页执行此查询时,您可能已经丢失了那些字符(即使您可以在屏幕上看到它们,它们在VARCHAR
变量中也不正确,或者如果您做了一个简单的SELECT
字面量).Windows(以及 .NET、SQL 服务器等)使用 UTF-16 Little Endian。
Í
字符 Latin Capital Letter I with Acute 存在于代码页 1252 和 UTF-16LE 中作为值 205(例如SELECT ASCII('Í'), CHAR(205);
),这就是为什么它当您删除encoding="utf-8"
以及为什么您没有 "lose" 那个字符时,可以通过将其放置在VARCHAR
文字和变量中来工作。但是,如该链接页面所示,UTF-8 编码中的字节序列为 195、141(是的,两个字节)。意思是,如果该字符确实是 UTF-8 编码的,那么在放入 UTF-16LE 环境中时就不会显示为该字符。XML 转换查看该字符的字节值 205(单字节,因为它当前是 VARCHAR 数据)并尝试提供与 that[=121 等效的 UTF-16LE =] 序列是 UTF-8。除了 205 本身在 UTF-8 中不存在。因此,您需要添加下一个字符,该字符是大写字母-"A",其值为 65。虽然 UTF-8 中有两个字节序列,但其中 none 是 205、65。这就是您收到
illegal xml character
错误的原因。由于屏幕上的文本必须是 UTF-16LE,如果源确实是 UTF-8,则必须将底层 UTF-8 字节序列转换为 UTF-16LE。
Í
的底层字节序列是 195, 141。因此我们可以通过执行以下操作从代码页 1252 的常规 ASCII 字符(因为这是当前 VARCHAR 数据)创建该序列:DECLARE @poit VARCHAR(100); SET @poit = '<?xml version="1.0" encoding="UTF-8"?><test>V' + CHAR(195) + CHAR(141) + 'A</test>'; SELECT CONVERT(XML, @poit);
Returns:
<test>VÍA</test>
数据还在
VARCHAR
和encoding="utf-8"
还在<xml>
元素!如果将数据保持为
VARCHAR
,则仅encoding=
值的以下更改有效:DECLARE @poit VARCHAR(100); SET @poit = '<?xml version="1.0" encoding="Windows-1252"?><test>VÍA</test>'; SELECT CONVERT(XML, @poit);
这假定源编码确实是 "Windows-1252",它是 Latin1_General 的 Microsoft 版本,它是 Latin1_General 归类的基础。
但是,如果 "encoding" 与当前数据库默认排序规则的代码页相同,则无需指定任何 VARCHAR 数据。
最后,
XML
服务器中的SQL数据是UTF-16LE,与NCHAR
和NVARCHAR
(和NTEXT
,但没有人应该再使用它了)。
结论
在将 XML 用作字符串(而不是
VARCHAR
)时使用NVARCHAR(MAX)
数据类型。对于没有任何改变的字符的字符串(即屏幕上的一切看起来都很完美),然后像您所做的那样简单地删除
encoding="utf-8"
。没有必要用UTF-16
替换它,因为值的本质是NVARCHAR
变量或文字(即以大写 -N
为前缀的字符串) .
关于使用 VARCHAR(MAX)
而不是 XML
甚至 NVARCHAR(MAX)
来保存 space,请记住 XML
数据类型在内部进行了优化,使得元素和属性名称仅在字典中存储一次,因此几乎没有 XML 的完全写出的字符串版本那么多的开销。因此,虽然 XML
类型确实将字符串存储为 UTF-16LE,但 if XML 文档有很多重复元素 and/or 属性名称,然后使用 XML
类型实际上可能比使用 VARCHAR(MAX)
:
DECLARE @ElementBased XML;
SET @ElementBased = (
SELECT * FROM master.sys.all_columns FOR XML PATH('Row')
);
DECLARE @AttributeBased XML;
SET @AttributeBased = (
SELECT * FROM master.sys.all_columns FOR XML RAW('Row')
);
SELECT @ElementBased AS [ElementBasedXML],
@AttributeBased AS [AttributeBasedXML],
DATALENGTH(@ElementBased) AS [ElementBasedXmlBytes],
DATALENGTH(CONVERT(VARCHAR(MAX), @ElementBased)) AS [ElementBasedVarCharBytes],
((DATALENGTH(@ElementBased) * 1.0) / DATALENGTH(CONVERT(VARCHAR(MAX), @ElementBased))
) * 100 AS [XmlElementSizeRelativeToVarcharElementSize],
DATALENGTH(@AttributeBased) AS [AttributeBasedXmlBytes],
DATALENGTH(CONVERT(VARCHAR(MAX), @AttributeBased)) AS [AttributeBasedVarCharBytes],
((DATALENGTH(@AttributeBased) * 1.0) /
DATALENGTH(CONVERT(VARCHAR(MAX), @AttributeBased))) * 100
AS [XmlAttributeSizeRelativeToVarCharAttributeSize];
Returns(至少在我的系统上):
ElementBasedXmlBytes 1717896
ElementBasedVarCharBytes 5889081
XmlElementSizeRelativeToVarcharElementSize 29.170867237180130482100
AttributeBasedXmlBytes 1544661
AttributeBasedVarCharBytes 3461864
XmlAttributeSizeRelativeToVarCharAttributeSize 44.619343798600984902900
如您所见,对于基于元素的 XML,XML
数据类型是 VARCHAR(MAX)
版本大小的 29%,对于基于属性的 XML,XML
数据类型是 VARCHAR(MAX)
版本大小的 44%。