【问题标题】:SQL Server: Replace invalid XML characters from a VARCHAR(MAX) fieldSQL Server:替换 VARCHAR(MAX) 字段中的无效 XML 字符
【发布时间】:2015-04-06 13:38:39
【问题描述】:

我有一个VARCHAR(MAX) 字段,它以XML 格式连接到外部系统。接口抛出以下错误:

mywebsite.com-2015-0202.xml:413005: parser error : xmlParseCharRef: invalid xmlChar value 29
ne and Luke's family in Santa Fe. You know you have a standing invitation,
                                                                               ^
mywebsite.com-2015-0202.xml:455971: parser error : xmlParseCharRef: invalid xmlChar value 25
The apprentice nodded, because frankly, who hadnt? That diseases like chol
                                                      ^
mywebsite.com.com-2015-0202.xml:456077: parser error : xmlParseCharRef: invalid xmlChar value 28
bon mot; a sentimental love of nature and animals; the proverbial British 
                                                                               ^
mywebsite.com-2015-0202.xml:472073: parser error : xmlParseCharRef: invalid xmlChar value 20
"Andyou want that?"
          ^
mywebsite.com-2015-0202.xml:492912: parser error : xmlParseCharRef: invalid xmlChar value 25
She couldnt live like this anymore.

我们发现以下字符列表无效:

�








	

























我正在尝试清理这些数据,我找到了一个 SQL 函数来清理这些字符here。但是,该函数将NVARCHAR(4000) 作为输入参数,因此我已将函数更改为使用VARCHAR(MAX)

如果将NVARCHAR(4000) 更改为VARCHAR(MAX) 会产生错误的结果,谁能告诉我?抱歉,我无法在本地测试此界面,因此想征求意见/建议。

原始功能:

CREATE FUNCTION fnStripLowAscii (@InputString nvarchar(4000))
RETURNS nvarchar(4000)
AS
BEGIN
IF @InputString IS NOT NULL
BEGIN
  DECLARE @Counter int, @TestString nvarchar(40)

  SET @TestString = '%[' + NCHAR(0) + NCHAR(1) + NCHAR(2) + NCHAR(3) + NCHAR(4) + NCHAR(5) + NCHAR(6) + NCHAR(7) + NCHAR(8) + NCHAR(11) + NCHAR(12) + NCHAR(14) + NCHAR(15) + NCHAR(16) + NCHAR(17) + NCHAR(18) + NCHAR(19) + NCHAR(20) + NCHAR(21) + NCHAR(22) + NCHAR(23) + NCHAR(24) + NCHAR(25) + NCHAR(26) + NCHAR(27) + NCHAR(28) + NCHAR(29) + NCHAR(30) + NCHAR(31) + ']%'

  SELECT @Counter = PATINDEX (@TestString, @InputString COLLATE Latin1_General_BIN)

  WHILE @Counter <> 0
  BEGIN
    SELECT @InputString = STUFF(@InputString, @Counter, 1, NCHAR(164))
    SELECT @Counter = PATINDEX (@TestString, @InputString COLLATE Latin1_General_BIN)
  END
END
RETURN(@InputString)
END

修改版:

CREATE FUNCTION [dbo].RemoveInvalidXMLCharacters (@InputString VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
    IF @InputString IS NOT NULL
    BEGIN
      DECLARE @Counter INT, @TestString NVARCHAR(40)

      SET @TestString = '%[' + NCHAR(0) + NCHAR(1) + NCHAR(2) + NCHAR(3) + NCHAR(4) + NCHAR(5) + NCHAR(6) + NCHAR(7) + NCHAR(8) + NCHAR(11) + NCHAR(12) + NCHAR(14) + NCHAR(15) + NCHAR(16) + NCHAR(17) + NCHAR(18) + NCHAR(19) + NCHAR(20) + NCHAR(21) + NCHAR(22) + NCHAR(23) + NCHAR(24) + NCHAR(25) + NCHAR(26) + NCHAR(27) + NCHAR(28) + NCHAR(29) + NCHAR(30) + NCHAR(31) + ']%'

      SELECT @Counter = PATINDEX (@TestString, @InputString COLLATE Latin1_General_BIN)

      WHILE @Counter <> 0
      BEGIN
        SELECT @InputString = STUFF(@InputString, @Counter, 1, ' ')
        SELECT @Counter = PATINDEX (@TestString, @InputString COLLATE Latin1_General_BIN)
      END
    END
    RETURN(@InputString)
END

【问题讨论】:

  • 听起来问题出在将字符串放入 XML 的地方-如果您使用的是不错的 XML 工具,它应该会自动执行所需的转义(或将文本包装在 @ 987654332@]]&gt; 块)。您不必重新实现此类功能。
  • @Damien_The_Unbeliever 不幸的是,这些“有问题的”XML 工具之一就是 SQL 本身。如果您在 SQL 查询中使用“FOR XML”将 NVARCHAR 数据转换为 XML,SQL 将很乐意将无效的 XML 字符作为其“预期”转义序列; SQL Server 生成 SQL Server 无法解析的 XML:\
  • 我正面临这个问题......仍然没有找到任何解决方案。就我而言,问题甚至更糟,因为我现在不能在手边可能包含无效字符的列。 :(
  • @VictorEspina,找到了一个可能对您有所帮助的解决方案,如果这仍然是一个悬而未决的问题。请在下面阅读我的回答。
  • @Damien_The_Unbeliever,我找到了解决此问题的方法,您可能会感兴趣...

标签: sql sql-server xml tsql sql-server-2012


【解决方案1】:

使用 VARBINARY 到 base64 并返回的隐式转换有一个技巧:

这里是你的邪恶清单

DECLARE @evilChars VARCHAR(MAX)=
  CHAR(0x0)
+ CHAR(0x1)
+ CHAR(0x2)
+ CHAR(0x3)
+ CHAR(0x4)
+ CHAR(0x5)
+ CHAR(0x6)
+ CHAR(0x7)
+ CHAR(0x8)
+ CHAR(0x9)
+ CHAR(0xa)
+ CHAR(0xb)
+ CHAR(0xc)
+ CHAR(0xd)
+ CHAR(0xe)
+ CHAR(0xf)
+ CHAR(0x10)
+ CHAR(0x11)
+ CHAR(0x12)
+ CHAR(0x13)
+ CHAR(0x14)
+ CHAR(0x15)
+ CHAR(0x16)
+ CHAR(0x17)
+ CHAR(0x18)
+ CHAR(0x19)
+ CHAR(0x1a)
+ CHAR(0x1b)
+ CHAR(0x1c)
+ CHAR(0x1d)
+ CHAR(0x1e)
+ CHAR(0x1f)
+ CHAR(0x7f);

这行得通

DECLARE @XmlAsString NVARCHAR(MAX)=
(
    SELECT @evilChars FOR XML PATH('test')
);
SELECT @XmlAsString;

结果(有些是“打印的”)

<test>&#x00;&#x01;&#x02;&#x03;&#x04;&#x05;&#x06;&#x07;&#x08;    
&#x0B;&#x0C;&#x0D;&#x0E;&#x0F;&#x10;&#x11;&#x12;&#x13;&#x14;&#x15;&#x16;&#x17;&#x18;&#x19;&#x1A;&#x1B;&#x1C;&#x1D;&#x1E;&#x1F;</test>

以下是禁止的

SELECT CAST(@XmlAsString AS XML)

但是你可以使用 VARBINARY 到 base64 的隐式转换

DECLARE @base64 NVARCHAR(MAX)=
(
    SELECT CAST(@evilChars AS VARBINARY(MAX)) FOR XML PATH('test')
);
SELECT @base64;

结果

<test>AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh9/</test>

现在您已经获得了包含特殊字符的真实 XML

SELECT CAST(CAST(@base64 AS XML).value('/test[1]','varbinary(max)') AS VARCHAR(MAX)) FOR XML PATH('reconverted')

结果

<reconverted>&#x0;&#x1;&#x2;&#x3;&#x4;&#x5;&#x6;&#x7;&#x8;  
&#xB;&#xC;
&#xE;&#xF;&#x10;&#x11;&#x12;&#x13;&#x14;&#x15;&#x16;&#x17;&#x18;&#x19;&#x1A;&#x1B;&#x1C;&#x1D;&#x1E;&#x1F;</reconverted>

【讨论】:

  • 你现在如何解码 xml 例如?
  • @DannyRancher,你会期待什么?当你 decode &amp;#00; 时,你试图 decode 什么? XML 中二进制内容的最佳格式是base64... 有了这个答案,我只想表明,可以创建包含这些实体的 XML。如果这是一个好主意取决于需求...
【解决方案2】:

使用VARCHAR(MAX) 是安全的,因为我的数据列是VARCHAR(MAX) 字段。此外,如果我将VARCHAR(MAX) 字段传递给接受NVARCHAR(MAX) 参数的SQL 函数,则将VARCHAR(MAX) 转换为NVARCHAR(MAX) 会产生开销。

非常感谢@RhysJones、@Damien_The_Unbeliever 的 cmets。

【讨论】:

    【解决方案3】:

    您需要使用nvarchar(max) 而不是varchar(max),否则更改就可以了。

    【讨论】:

    • 是否将VARCHAR(MAX) 字段传递给接受NVARCHAR(MAX) 参数的SQL 函数不会导致性能(转换开销)问题?
    • 是的,使用 nvarchar 会产生开销。但是,XML 是一种 Unicode 格式,因此它应该真正存储在 nvarchar 中。如果您的数据是 varchar,您可以安全地使用 varchar(max) 作为函数。
    猜你喜欢
    • 1970-01-01
    • 2013-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-16
    • 1970-01-01
    相关资源
    最近更新 更多