【问题标题】:Handling XML Parsing errors in T-SQL处理 T-SQL 中的 XML 解析错误
【发布时间】:2021-05-07 23:59:40
【问题描述】:

我正在尝试创建一个审核传入 XML 数据馈送的报告。 我们根据系统中的信息验证传入交易,仅当交易与有效记录上的某些数据点匹配时才插入交易数据。

我的报告查看所有未通过此验证的近期传入交易,然后尝试从每笔交易中读取一些数据,以便我们可以手动检查问题并要求我们的供应商更正信息并重新发送。

有时,我们会得到一些无效的 xml。有时它被截断,有时它缺少标签等。 当 xml 有效时,我的报告工作正常,但是当遇到带有无效 xml 的事务时,查询完全出错。

我需要升级我的代码,以便整个查询在遇到无效 xml 时不会出错。 理想情况下,当遇到无效的 xml 时,它会通过将所有列拉为“NULL”来处理这个问题,或者更好的是,拉入 xml 的好的部分(正确关闭的标签)。

通常我得到:“XML 解析:第 9 行,字符 0,输入意外结束”

这是我查询的当前结构的模型(注意第二个 XML 文件无效/被截断):

DECLARE @mockup TABLE(ID INT, xmlcontent XML);
INSERT INTO @mockup VALUES

(1, '
<Movie>
<MovieID>1234</MovieID>
<MovieName>Mission Impossible</MovieName>
<Character>
    <FirstName>Ethan</FirstName>
    <LastName>Hunt</LastName>
</Character>
</Movie>'),

(2,'
<Movie>
<MovieID>5678</MovieID>
<MovieName>Casino Royale</MovieName>
<Character>
    <FirstName>James</FirstName>
    <LastName>Bond</LastName>
')

SELECT
ID,
allnodes.value('(MovieID)[1]','nvarchar(100)') as MovieID,
allnodes.value('(MovieName)[1]','nvarchar(100)') as MovieName,
allnodes.value('(Character/FirstName)[1]','nvarchar(100)') as FirstName,
allnodes.value('(Character/LastName)[1]','nvarchar(100)') as LastName

FROM @mockup mockup
--Get all the transaction data:
CROSS APPLY mockup.xmlcontent.nodes('Movie') as xmldata(allnodes)

【问题讨论】:

    标签: xml tsql sqlxml


    【解决方案1】:

    错误说明

    您问题中的示例代码确实返回了“unexpected end of input” 错误,但该错误源自插入。由于表定义中的XML 数据类型(表变量@mockup),SQL Server 引擎会验证输入值是否为格式正确的 XML 数据。

    解决方案

    如果您想首先提取所有数据 - 有效的无效的 XML - 那么您必须将表列数据类型从 XML 更改为类似 nvarchar(1000)

    然后可以尝试使用TRY_CONVERT() function 处理无效的 XML。如果转换失败,此函数将返回NULL。所以不需要部分处理:这将需要一个更长的解决方案来手动解析字符串类型数据并完成缺失的 XML 标记……SQL Server 不是此类文本解析和操作的主要候选者。

    create table mockup
    (
      ID INT,
      xmlcontent nvarchar(1000) --- changed type
    );
    
    INSERT INTO mockup (ID, xmlcontent) VALUES
    (1,
    '<Movie>
      <MovieID>1234</MovieID>
      <MovieName>Mission Impossible</MovieName>
      <Character>
        <FirstName>Ethan</FirstName>
        <LastName>Hunt</LastName>
      </Character>
    </Movie>'),
    (2,
    '<Movie>
      <MovieID>5678</MovieID>
      <MovieName>Casino Royale</MovieName>
      <Character>
        <FirstName>James</FirstName>
        <LastName>Bond</LastName>');
    
    with cte as
    (
      select m.id,
             try_convert(xml, m.xmlcontent) xmldata -- introduced TRY_CONVERT()
      from mockup m
    )
    select c.id,
           xmlmovie.node.value('(MovieID)[1]', 'nvarchar(100)') as MovieID,
           xmlmovie.node.value('(MovieName)[1]','nvarchar(100)') as MovieName,
           xmlmovie.node.value('(Character/FirstName)[1]','nvarchar(100)') as FirstName,
           xmlmovie.node.value('(Character/LastName)[1]','nvarchar(100)') as LastName
    from cte c
    outer apply c.xmldata.nodes('Movie') as xmlmovie(node); -- changed to OUTER apply to show NULL values
    

    结果

    id  MovieID  MovieName           FirstName  LastName
    --  -------  ------------------  ---------  --------
    1  1234      Mission Impossible  Ethan      Hunt
    2  null      null                null       null
    

    Fiddle 查看插入和工作解决方案上重现的错误。

    【讨论】:

    • 所以看起来这会起作用,除了我刚刚发现我正在使用的数据库的兼容性级别为 100 (2012); 110 是 TRY_CONVERT() 的最低级别,我们的 dba 告诉我他们不会升级兼容性级别,因为这个 db 服务于一些遗留应用程序,如果它设置更高的兼容性可能会中断。任何备份解决方案?
    • Compatibility level 100 将是 SQL Server 2008 (R2)。您可以尝试通过XML Schema Collections 使用手动 XSD 验证进行编程,该验证在 SQL Server 2005 中仍然存在,如this post 中所示。处理 XML 解析错误可能会给可行的解决方案带来问题:-/
    猜你喜欢
    • 2011-03-11
    • 2016-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-03
    • 1970-01-01
    • 2012-10-10
    • 2023-04-04
    相关资源
    最近更新 更多