2011-03-21 13 views
13

Un componente externo está llenando una columna nvarchar en una tabla con algunos valores. La mayoría de las veces es una cadena legible por humanos, pero ocasionalmente es XML (en el caso de algunas excepciones internas en la compilación de terceros).En SQL Server, ¿cuál es la mejor manera de determinar si una cadena dada es un XML válido o no?

Como una solución temporal (hasta que lo arreglen y usen una cadena siempre), me gustaría analizar los datos XML y extraer el mensaje real.

Entorno: SQL Server 2005; las cadenas tienen siempre menos de 1K de tamaño; podría haber algunos miles de filas en esta tabla.


me encontré con un par de soluciones, pero no estoy seguro de si son lo suficientemente bueno:

  1. invocación sp_xml_preparedocument procedimiento almacenado y se envuelve alrededor del bloque try/catch. Verifique el valor/mango de retorno.
  2. Escribir el código administrado (en C#), una vez más el manejo de excepciones y ver si es una cadena válida.

Ninguno de estos métodos parece eficiente. Estaba buscando algo similar a ISNUMERIC(): una función ISXML(). ¿Hay alguna otra forma mejor de verificar la cadena?

+0

¿Por qué cuando 2 soluciones no son suficientes? ¿Qué faltaba? – rene

+1

Si la cadena no comienza con '<' entonces, presumiblemente, usted sabe que definitivamente no es XML. –

+1

@rene: Quise decir "eficiente" :) Las dos soluciones anteriores no suenan escalables si el número de filas aumenta sustancialmente. – Venkat

Respuesta

8

I would like to parse the XML data and extract the actual message.

Quizás no sea necesario comprobar si hay un XML válido. Puede verificar la presencia de la etiqueta xml correspondiente con charindex en una declaración de caso y extraer el mensaje de error usando substring.

Aquí hay un ejemplo con una cadena XML simplificada, pero creo que se entiende la idea.

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 

Resultado:

No xml value 1 
No xml value 2 
Text value in xml 
+0

Txs! Esta podría no ser la solución para todos estos escenarios, pero se ve bien para mi caso, ¡como una solución temporal simple! Además, intentaré usarlo como un simple SQL en línea en lugar de envolverlo en una función. Lo voy a intentar :) – Venkat

1

No sé acerca de una mejor manera, pero aquí es una manera:

DECLARE @table TABLE (myXML XML) 

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

SELECT myXML 
FROM @table 
FOR XML RAW 

Si el XML es válido que generará un error:

DECLARE @table TABLE (myXML XML) 

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

SELECT myXML 
FROM @table 
FOR XML RAW 

Sólo una aclaración , todo lo que tienes que hacer es echarlo:

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

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; 
+0

¿Cómo se aplicaría esto al caso del OP en el que tienen una carga de cadenas que pueden ser o no XML y que solo desean convertir como XML las que son válidas para emitir? 'select cast ('foo' como xml)' no es 'xml', pero se lanza sin error. 'select cast ('foo

+0

Lo modifiqué para que sea más como una función. Puede hacer que su función isXML acepte un varchar y devolver si es XML válido o no. A continuación, puede hacer lo que desee con el XML válido. – SQLMason

+0

No se puede usar 'try..catch' en las funciones. –

4

Puede crear un esquema XML y usarlo para validar contra las cadenas XML.

ver aquí para información adicional: http://msdn.microsoft.com/en-us/library/ms176009.aspx

He aquí un ejemplo:

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>' 

Ejemplos:

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

Msg 6965, nivel 16, estado 1, línea 2 Validación XML: Contenido inválido Elemento (s) esperado (s): ID de usuario donde se especificó el elemento 'Nombre de usuario'. Ubicación:/: Usuario [1]/: Nombre de usuario [1]

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

Msg 6909, nivel 16, estado 1, línea 2 XML Validación: nodo de texto no está permitido en este lugar, el tipo era definido con elemento solo contenido o con contenido simple.Ubicación:/

+0

Estaba buscando un XML válido, no en contra de un esquema. De lo contrario, tu respuesta es la misma que la mía. – SQLMason

2

Sobre la base de la respuesta aceptada, he creado este para comprobar XML válido y convertir opcionalmente la cadena de entrada a XML (o extraer los elementos requeridos/atributos del XML) , porque me di cuenta de que TRY_CONVERT funciona correctamente en caso de que acaba de pasar en texto sin formato, lo que no esperaba, por lo que era necesario otro control para evitar el lanzamiento final a XML que necesito para trabajar en caso de que la columna texto (fila de muestra 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 ; 
Cuestiones relacionadas