В SQL Server, как лучше всего определить, является ли данная строка допустимым XML или нет? - PullRequest
15 голосов
/ 21 марта 2011

Сторонний компонент заполняет столбец nvarchar в таблице некоторыми значениями. В большинстве случаев это читаемая человеком строка, но иногда это XML (в случае некоторых внутренних исключений в сторонних компах).

В качестве временного решения (пока они не исправят это и не будут всегда использовать строку), я хотел бы проанализировать данные XML и извлечь фактическое сообщение.

Среда: SQL Server 2005; строки всегда меньше 1 КБ; в этой таблице может быть несколько тысяч строк.


Я натолкнулся на пару решений, но я не уверен, что они достаточно хороши:

  1. Вызвать sp_xml_preparedocument сохраненный процесс и обернуть его вокруг блока TRY / CATCH. Проверьте возвращаемое значение / ручку.
  2. Написать управляемый код (в C #), снова обработать исключительную ситуацию и посмотреть, является ли она допустимой строкой.

Ни один из этих методов не кажется эффективным. Я искал что-то похожее на ISNUMERIC(): функцию ISXML(). Есть ли другой лучший способ проверки строки?

Ответы [ 4 ]

9 голосов
/ 22 марта 2011

Я хотел бы проанализировать данные XML и извлечь фактическое сообщение.

Возможно, нет необходимости проверять действительный XML. Вы можете проверить наличие соответствующего тега xml с charindex в операторе case и извлечь сообщение об ошибке, используя 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
5 голосов
/ 02 мая 2017

Основываясь на принятом ответе, я создал его для проверки правильности 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 ;
4 голосов
/ 21 марта 2011

Вы можете создать схему 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: недопустимое содержимое.Ожидаемый элемент (ы): UserID, где был указан элемент 'UserName'.Расположение: / : пользователь [1] / : имя пользователя [1]

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

Сообщение 6909, уровень 16, состояние 1, строка 2 Проверка XML: текстовый узел на этом запрещенlocation, тип был определен с содержимым только для элемента или с простым содержимым.Расположение: /

1 голос
/ 21 марта 2011

Я не знаю, как лучше, но вот способ:

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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...