在SQL Server中,确定给定字符串是否为有效XML的最佳方法是什么?

在SQL Server中,确定给定字符串是否为有效XML的最佳方法是什么?,xml,sql-server-2005,tsql,Xml,Sql Server 2005,Tsql,第三方组件正在用一些值填充表中的nvarchar列。大多数情况下,它是一个人类可读的字符串,但有时它是XML,以防第三方公司出现内部异常 作为一个临时解决方案,在他们修复它并始终使用字符串之前,我希望解析XML数据并提取实际的消息 环境:SQLServer2005;字符串的大小始终小于1K;这个表中可能有几千行 我遇到了一些解决方案,但我不确定它们是否足够好: 调用sp_xml_preparedocument存储过程并将其包装在TRY/CATCH块周围。检查返回值/句柄。 用C编写托管代码,再次

第三方组件正在用一些值填充表中的nvarchar列。大多数情况下,它是一个人类可读的字符串,但有时它是XML,以防第三方公司出现内部异常

作为一个临时解决方案,在他们修复它并始终使用字符串之前,我希望解析XML数据并提取实际的消息

环境:SQLServer2005;字符串的大小始终小于1K;这个表中可能有几千行

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

调用sp_xml_preparedocument存储过程并将其包装在TRY/CATCH块周围。检查返回值/句柄。 用C编写托管代码,再次执行异常处理,并查看它是否为有效字符串。
这些方法似乎都没有效率。我在寻找类似于ISNUMERIC的东西:一个ISXML函数。还有其他更好的检查字符串的方法吗?

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

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;
vs


您可以创建一个XML模式,并使用它根据XML字符串进行验证

有关更多信息,请参见此处:

下面是一个例子:

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>'
Msg 6909,16级,状态1,第2行 XML验证:此位置不允许使用文本节点,该类型是使用仅元素内容或简单内容定义的。地点:/

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

也许没有必要检查有效的XML。您可以在case语句中使用charindex检查是否存在适当的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

基于已接受的答案,我创建了它来检查有效的XML,并可以选择将输入字符串转换为XML,或从XML中提取所需的元素/属性,因为我发现如果您只是传入纯文本,它会成功工作,这是我没有想到的,因此,需要进行另一项检查,以防止最终转换为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 ;

为什么你的两个解决方案都不够?缺少什么?如果字符串不是以@rene开头:我的意思是有效的:如果行数大幅增加,上述两种解决方案听起来不可伸缩。请参见下面的TRY_CONVERT答案。如果OP的字符串负载可能是XML,也可能不是XML,并且他们只希望将其转换为XML,那么这会适用于OP的情况吗你可以投吗?选择cast'foo',因为xml不是xml,而是无错误的强制转换。选择cast'fooDECLARE @z XML(UserSchemaCollection) SELECT @z = 'Some text'
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
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 ;