处理 T-SQL 中的 XML 解析错误

问题描述

我正在尝试创建一个报告来审核传入的 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)中的 @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 以查看在插入和工作解决方案中重现的错误。