【问题标题】:In SQL Server, what is the best way to determine if a given string is a valid XML or not?在 SQL Server 中,确定给定字符串是否为有效 XML 的最佳方法是什么?
【发布时间】:2011-07-19 21:45:14
【问题描述】:

第三方组件正在用一些值填充表中的nvarchar 列。大多数时候它是人类可读的字符串,但偶尔它是 XML(以防 3rd 方 comp 中的一些内部异常)。

作为临时解决方案(直到他们修复它并始终使用字符串),我想解析 XML 数据并提取实际消息。

环境: SQL Server 2005;字符串的大小总是小于 1K;此表中可能有几千行。


我遇到了几个解决方案,但我不确定它们是否足够好:

  1. 调用 sp_xml_preparedocument 存储过程并将其包装在 TRY/CATCH 块周围。检查返回值/句柄。
  2. 编写托管代码(在 C# 中),再次处理异常并查看它是否为有效字符串。

这些方法似乎都没有效率。我正在寻找类似于ISNUMERIC() 的东西:ISXML() 函数。还有其他更好的检查字符串的方法吗?

【问题讨论】:

  • 为什么你的 2 个解决方案不够用?缺少什么?
  • 如果字符串不以< 开头,那么您大概知道它肯定不是XML。
  • @rene:我的意思是“高效” :) 如果行数大幅增加,上述 2 个解决方案听起来无法扩展。
  • 查看下面的 TRY_CONVERT 答案

标签: xml sql-server-2005 tsql


【解决方案1】:

我想解析 XML 数据并提取实际消息。

也许不需要检查有效的 XML。您可以使用charindex 在case 语句中检查是否存在适当的xml 标记,并使用substring 提取错误消息。

这是一个带有简化 XML 字符串的示例,但我想你明白了。

declare @T table(ID int, Col1 nvarchar(1000))

insert into @T values
(1, 'No xml value 1'),
(2, 'No xml value 2'),
(3, '<root><item>Text value in xml</item></root>')

select
  case when charindex('<item>', Col1) = 0
  then Col1
  else
    substring(Col1, charindex('<item>', Col1)+6, charindex('</item>', Col1)-charindex('<item>', Col1)-6)
  end  
from @T

结果:

No xml value 1
No xml value 2
Text value in xml

【讨论】:

  • 发送!这可能不是所有此类情况的解决方案,但对于我的情况来说看起来不错,作为一个简单的临时修复!另外,我将尝试将其用作简单的内联 SQL,而不是将其包装在函数中。去试试看:)
【解决方案2】:

根据接受的答案,我创建了它来检查有效的 XML,并可选择将输入字符串转换为 XML(或从 XML 中提取所需的元素/属性),因为我发现 TRY_CONVERT 如果您只传入纯文本,则可以成功工作,这是我没想到的,因此需要进行另一项检查以防止最终转换为 XML 我需要工作以防源列仅包含一些文本(示例第 1 行) :

declare @T table(ID int, Col1 nvarchar(1000))
insert into @T values
(1, 'random text value 1'),
(2, '<broken> or invalid xml value 2'),
(3, '<root><item>valid xml</item></root>')
select id, Col1,
 Converted_XML = CASE 
        when [Col1] IS NULL THEN NULL                   /* NULL stays NULL */ 
        when TRY_CONVERT(xml, [Col1]) is null THEN NULL /* Xml Document Error */
        when CHARINDEX('<', [Col1]) < 1 AND CHARINDEX('>', [Col1]) < 1 THEN NULL        /* no xml */
        else CONVERT(xml, [Col1])                       /* Parsing succesful. => in this case you can convert string to XML and/or extract the values */
    END,
    Result_Comment = CASE 
        when [Col1] IS NULL THEN 'NULL always stays NULL'
        when TRY_CONVERT(xml, [Col1]) is null THEN 'Xml Document Error'
        when CHARINDEX('<', [Col1]) < 1 AND CHARINDEX('>', [Col1]) < 1 THEN 'no xml'
        else [Col1]
    END
FROM @T ;

【讨论】:

  • 很好的解决方案。只是需要注意的是,有时 SQL 会将带有无效 xml 的字符串解释为混合内容。因此,如果项目的父项是 但起始括号 'valild><item>valid xml</item> = valid&amp;gt;&lt;item&gt;xml&lt;/item&gt; 只是需要注意的事情。
【解决方案3】:

您可以创建一个 XML 模式并使用它来验证 XML 字符串。

更多信息请看这里:http://msdn.microsoft.com/en-us/library/ms176009.aspx

这是一个例子:

CREATE XML SCHEMA COLLECTION UserSchemaCollection AS 
N'<?xml version="1.0" encoding="UTF-16"?>
  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name = "User" >
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name = "UserID" />
                <xsd:element name = "UserName" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
  </xsd:schema>';


DECLARE @x XML(UserSchemaCollection)
SELECT @x = '<User><UserID>1234</UserID><UserName>Sebastian</UserName></User>'

例子:

DECLARE @y XML(UserSchemaCollection)
SELECT @y = '<User><UserName>Sebastian</UserName></User>'

消息 6965,第 16 级,状态 1,第 2 行 XML 验证:无效的内容。预期元素:指定元素“用户名”的用户 ID。位置:/:User[1]/:UserName[1]

DECLARE @z XML(UserSchemaCollection)
SELECT @z = 'Some text'

消息 6909,第 16 级,状态 1,第 2 行 XML 验证:此位置不允许使用文本节点,类型是使用仅元素内容或简单内容定义的。地点:/

【讨论】:

  • 他只是在检查有效的 XML,而不是针对方案。否则你的答案和我的一样。
【解决方案4】:

我不知道最好的方法,但这里有一个方法:

DECLARE @table TABLE (myXML XML)

INSERT INTO @table
SELECT  
'
    <Employee>
        <FirstName>Henry</FirstName>
        <LastName>Ford</LastName>
    </Employee>
'

SELECT myXML 
FROM @table 
FOR XML RAW

如果 XML 无效,则会抛出错误:

DECLARE @table TABLE (myXML XML)

INSERT INTO @table
SELECT  
'
    <Employee
        <FirstName>Henry</FirstName>
        <LastName>Ford</LastName>
    </Employee>
'

SELECT myXML 
FROM @table 
FOR XML RAW

为了澄清,你所要做的就是投射它:

BEGIN TRY
    DECLARE @myXML XML
    SET @myXML = CAST
    ('
        <Employee>
            <FirstName>Henry</FirstName>
            <LastName>Ford</LastName>
        </Employee>
    ' AS XML)
    SELECT 'VALID XML'
END TRY
BEGIN CATCH
    SELECT 'INVALID XML'
END CATCH;

BEGIN TRY
    DECLARE @myXML XML
    SET @myXML = CAST
    ('
        <Employee
            <FirstName>Henry</FirstName>
            <LastName>Ford</LastName>
        </Employee>
    ' AS XML)
    SELECT 'VALID XML'
END TRY
BEGIN CATCH
    SELECT 'INVALID XML'
END CATCH;

【讨论】:

  • 这将如何适用于 OP 的情况,即他们有大量可能是也可能不是 XML 的字符串,并且他们只想将有效转换为 XML 的字符串转换为 XML? select cast('foo' as xml) 不是 xml 而是无错误地转换。 select cast('foo &lt; bar' as xml) 导致错误。
  • 我对其进行了修改,使其更像一个函数。您可以让您的 isXML 函数接受一个 varchar 并返回它是否是有效的 XML。然后你可以对有效的 XML 做你想做的事。
  • 你不能在函数中使用try..catch
  • 返回值的过程,对不起。
  • 看起来没有 RBAR 的好方法。
猜你喜欢
  • 2010-09-05
  • 2010-10-23
  • 2023-01-10
  • 2018-07-10
  • 2010-10-19
  • 1970-01-01
  • 1970-01-01
  • 2012-03-13
  • 1970-01-01
相关资源
最近更新 更多