Compartir por


Construcción de XML (XQuery)

Se aplica a:SQL Server

En XQuery, puede usar los constructores directos y calculados para construir estructuras XML dentro de una consulta.

Nota

No hay ninguna diferencia entre los constructores directos y calculados .

Utilizar constructores directos

Cuando se utilizan constructores directos, se especifica una sintaxis parecida a XML al construir el XML. En los ejemplos siguientes se muestra la construcción de XML mediante los constructores directos.

Construir elementos

Cuando se utilizan notaciones XML, se pueden construir elementos. En el ejemplo siguiente se usa la expresión del constructor de elementos directos y se crea un <elemento ProductModel> . El elemento construido tiene tres elementos secundarios

  • Un nodo de texto.

  • Dos nodos de elemento, <Resumen> y <características>.

    • El <elemento Summary> tiene un elemento secundario de nodo de texto cuyo valor es "Some description".

    • El <elemento Features> tiene tres elementos secundarios del nodo de elementos, <Color>, <Weight> y <Warranty>. Cada uno de estos nodos tiene un nodo de texto secundario y los valores Red, 25 y 2 years parts and labor respectivamente.

declare @x xml;  
set @x='';  
select @x.query('<ProductModel ProductModelID="111">;  
This is product model catalog description.  
<Summary>Some description</Summary>  
<Features>  
  <Color>Red</Color>  
  <Weight>25</Weight>  
  <Warranty>2 years parts and labor</Warranty>  
</Features></ProductModel>')  
  

El XML resultante es el siguiente:

<ProductModel ProductModelID="111">  
  This is product model catalog description.  
  <Summary>Some description</Summary>  
  <Features>  
    <Color>Red</Color>  
    <Weight>25</Weight>  
    <Warranty>2 years parts and labor</Warranty>  
  </Features>  
</ProductModel>  

Aunque la construcción de elementos a partir de expresiones constantes, tal como se muestra en este ejemplo, resulta útil, la auténtica ventaja de esta característica del lenguaje XQuery es la capacidad de construir XML que extrae datos de una base de datos de forma dinámica. Puede utilizar llaves para especificar expresiones de consulta. En el XML resultante, se sustituye la expresión por su valor. Por ejemplo, la consulta siguiente construye un <NewRoot> elemento con un elemento secundario ().<e> El valor del elemento <e> se calcula especificando una expresión de ruta dentro de llaves ("{ ... }").

DECLARE @x xml;  
SET @x='<root>5</root>';  
SELECT @x.query('<NewRoot><e> { /root } </e></NewRoot>');  

Las llaves actúan como tokens de cambio de contexto y cambian la consulta de construcción de XML a evaluación de consulta. En este caso, se evalúa la expresión de ruta de acceso de XQuery entre llaves, /root, y se sustituyen los resultados por la misma.

El resultado es el siguiente:

<NewRoot>  
  <e>  
    <root>5</root>  
  </e>  
</NewRoot>  

La consulta siguiente es similar a la anterior. Sin embargo, la expresión de las llaves especifica la función data() para recuperar el valor atómico del <root> elemento y lo asigna al elemento construido, . <e>

DECLARE @x xml;  
SET @x='<root>5</root>';  
DECLARE @y xml;  
SET @y = (SELECT @x.query('  
                           <NewRoot>  
                             <e> { data(/root) } </e>  
                           </NewRoot>' ));  
SELECT @y;  

El resultado es el siguiente:

<NewRoot>  
  <e>5</e>  
</NewRoot>  

Si desea utilizar llaves como parte del texto en lugar de como token de cambio de contexto, debe utilizarlas con valores de escape como "}}" o "{{", tal como se muestra en el ejemplo siguiente:

DECLARE @x xml;  
SET @x='<root>5</root>';  
DECLARE @y xml;  
SET @y = (SELECT @x.query('  
<NewRoot> Hello, I can use {{ and  }} as part of my text</NewRoot>'));  
SELECT @y;  

El resultado es el siguiente:

<NewRoot> Hello, I can use { and  } as part of my text</NewRoot>  

La consulta siguiente es otro ejemplo de construcción de elementos mediante el constructor de elemento directo. Además, el valor del <FirstLocation> elemento se obtiene ejecutando la expresión en las llaves. La expresión de consulta devuelve los pasos de fabricación de la primera ubicación de centro de trabajo de la columna Instructions de la tabla Production.ProductModel.

SELECT Instructions.query('  
    declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";  
        <FirstLocation>  
           { /AWMI:root/AWMI:Location[1]/AWMI:step }  
        </FirstLocation>   
') as Result   
FROM Production.ProductModel  
WHERE ProductModelID=7;  

El resultado es el siguiente:

<FirstLocation>  
  <AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">  
      Insert <AWMI:material>aluminum sheet MS-2341</AWMI:material> into the <AWMI:tool>T-85A framing tool</AWMI:tool>.   
  </AWMI:step>  
  <AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">  
      Attach <AWMI:tool>Trim Jig TJ-26</AWMI:tool> to the upper and lower right corners of the aluminum sheet.   
  </AWMI:step>  
   ...  
</FirstLocation>  

Contenido de elemento en la construcción de XML

En el ejemplo siguiente se ilustra el comportamiento de las expresiones en la construcción de contenido de elemento mediante el constructor de elemento directo. En el ejemplo siguiente, el constructor de elemento directo especifica una expresión. Para esta expresión, se crea un nodo de texto en el XML resultante.

declare @x xml;  
set @x='  
<root>  
  <step>This is step 1</step>  
  <step>This is step 2</step>  
  <step>This is step 3</step>  
</root>';  
select @x.query('  
<result>  
 { for $i in /root[1]/step  
    return string($i)  
 }  
</result>');  
  

La secuencia de valores atómicos resultante de la evaluación de la expresión se agrega al nodo de texto con un espacio adicional entre los valores atómicos adyacentes, tal como se muestra en el resultado. El elemento construido tiene un elemento secundario. Se trata de un nodo de texto que incluye el valor que se muestra en el resultado.

<result>This is step 1 This is step 2 This is step 3</result>  

Si en lugar de una expresión se especifican tres expresiones independientes que generan tres nodos de texto, los nodos de texto adyacentes se combinarán en uno solo, mediante concatenación, en el XML resultante.

declare @x xml;  
set @x='  
<root>  
  <step>This is step 1</step>  
  <step>This is step 2</step>  
  <step>This is step 3</step>  
</root>';  
select @x.query('  
<result>  
 { string(/root[1]/step[1]) }  
 { string(/root[1]/step[2]) }  
 { string(/root[1]/step[3]) }  
</result>');  

El nodo del elemento construido tiene un elemento secundario. Se trata de un nodo de texto que incluye el valor que se muestra en el resultado.

<result>This is step 1This is step 2This is step 3</result>  

Construir atributos

Cuando se construyen elementos mediante el constructor de elemento directo, también se pueden especificar atributos del elemento mediante sintaxis parecida a XML, tal como se muestra en este ejemplo:

declare @x xml;  
set @x='';  
select @x.query('<ProductModel ProductModelID="111">;  
This is product model catalog description.  
<Summary>Some description</Summary>  
</ProductModel>')  

El XML resultante es el siguiente:

<ProductModel ProductModelID="111">  
  This is product model catalog description.  
  <Summary>Some description</Summary>  
</ProductModel>  

El elemento <ProductModel> construido tiene un atributo ProductModelID y estos nodos secundarios:

  • Un nodo de texto, This is product model catalog description.

  • Un nodo de elemento, <Summary>. Este nodo tiene un nodo de texto secundario, Some description.

Cuando se construye un atributo, se puede especificar su valor con una expresión entre llaves. En este caso, el resultado de la expresión se devuelve como el valor del atributo.

En el ejemplo siguiente, la función data() no es estrictamente necesaria. Dado que está asignando el valor de expresión a un atributo, data() se aplica implícitamente para recuperar el valor con tipo de la expresión especificada.

DECLARE @x xml;  
SET @x='<root>5</root>';  
DECLARE @y xml;  
SET @y = (SELECT @x.query('<NewRoot attr="{ data(/root) }" ></NewRoot>'));  
SELECT @y;  

El resultado es el siguiente:

<NewRoot attr="5" />  

A continuación se muestra otro ejemplo en el cual se especifican expresiones para la construcción de los atributos LocationID y SetupHrs. Estas expresiones se evalúan con el XML en la columna Instruction. El valor con tipo de la expresión se asigna a los atributos.

SELECT Instructions.query('  
    declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";  
        <FirstLocation   
         LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"  
         SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >  
           { /AWMI:root/AWMI:Location[1]/AWMI:step }  
        </FirstLocation>   
') as Result   
FROM  Production.ProductModel  
where ProductModelID=7;  

Éste es el resultado parcial:

<FirstLocation LocationID="10" SetupHours="0.5" >  
  <AWMI:step ...   
  </AWMI:step>  
  ...  
</FirstLocation>  

Limitaciones de la implementación

Éstas son las limitaciones:

  • No se admiten varias expresiones de atributos o las mixtas (cadena y expresión XQuery). Por ejemplo, tal como se muestra en la consulta siguiente, se construye XML en el que Item es una constante y el valor 5 se obtiene evaluando una expresión de consulta:

    <a attr="Item 5" />  
    

    La consulta siguiente devuelve un error porque se mezcla una cadena de constantes con una expresión ({/x}), algo que no se admite:

    DECLARE @x xml  
    SET @x ='<x>5</x>'  
    SELECT @x.query( '<a attr="Item {/x}"/>' )   
    

    En este caso, tiene las opciones siguientes:

    • Formar el valor de atributo concatenando dos valores atómicos. Estos valores atómicos se serializan en el valor del atributo con un espacio entre los mismos:

      SELECT @x.query( '<a attr="{''Item'', data(/x)}"/>' )   
      

      El resultado es el siguiente:

      <a attr="Item 5" />  
      
    • Use la función concat para concatenar los dos argumentos de cadena en el valor de atributo resultante:

      SELECT @x.query( '<a attr="{concat(''Item'', /x[1])}"/>' )   
      

      En este caso, no se agrega un espacio entre los dos valores de cadena. Si desea agregarlo, debe indicarlo explícitamente.

      El resultado es el siguiente:

      <a attr="Item5" />  
      
  • No se admiten varias expresiones como valor de un atributo. Por ejemplo, la consulta siguiente genera un error:

    DECLARE @x xml  
    SET @x ='<x>5</x>'  
    SELECT @x.query( '<a attr="{/x}{/x}"/>' )  
    
  • No se admiten las secuencias heterogéneas. Si se intenta asignar una secuencia heterogénea como valor de atributo, se devolverá un error, como se muestra en el ejemplo siguiente. En este ejemplo, una secuencia heterogénea, una cadena "Item" y un elemento <x>, se especifican como el valor del atributo:

    DECLARE @x xml  
    SET @x ='<x>5</x>'  
    select @x.query( '<a attr="{''Item'', /x }" />')  
    

    Si aplica la función data(), la consulta funciona porque recupera el valor atómico de la expresión, /x, que se concatena con la cadena. A continuación se muestra una secuencia de valores atómicos:

    SELECT @x.query( '<a attr="{''Item'', data(/x)}"/>' )   
    

    El resultado es el siguiente:

    <a attr="Item 5" />  
    
  • El orden del nodo de atributo se aplica durante la serialización, y no durante la comprobación de tipos estáticos. Por ejemplo, en la consulta siguiente se produce un error porque ésta intenta agregar un atributo después de un nodo que no es un atributo.

    select convert(xml, '').query('  
    element x { attribute att { "pass" }, element y { "Element text" }, attribute att2 { "fail" } }  
    ')  
    go  
    

    La consulta del ejemplo anterior devuelve el siguiente error:

    XML well-formedness check: Attribute cannot appear outside of element declaration. Rewrite your XQuery so it returns well-formed XML.  
    

Agregar espacios de nombres

Cuando se construye XML mediante los constructores directos, es posible crear los nombres del elemento y el atributo construidos como completos mediante un prefijo de espacio de nombres. Puede enlazar el prefijo al espacio de nombres de los modos siguientes:

  • Mediante un atributo de declaración de espacio de nombres.

  • Mediante la cláusula WITH XMLNAMESPACES.

  • En el prólogo de las consultas XQuery.

Utilizar un atributo de declaración de espacio de nombres para agregar espacios de nombres

En el ejemplo siguiente se usa un atributo de declaración de espacio de nombres en la construcción del elemento <a> para declarar un espacio de nombres predeterminado. La construcción del elemento <b> secundario deshace la declaración del espacio de nombres predeterminado declarado en el elemento primario.

declare @x xml  
set @x ='<x>5</x>'  
select @x.query( '  
  <a xmlns="a">  
    <b xmlns=""/>  
  </a>' )   

El resultado es el siguiente:

<a xmlns="a">  
  <b xmlns="" />  
</a>  

Puede asignar un prefijo al espacio de nombres. El prefijo se especifica en la construcción del elemento <a>.

declare @x xml  
set @x ='<x>5</x>'  
select @x.query( '  
  <x:a xmlns:x="a">  
    <b/>  
  </x:a>' )  

El resultado es el siguiente:

<x:a xmlns:x="a">  
  <b />  
</x:a>  

Se puede anular la declaración de un espacio de nombres predeterminado en la construcción de XML, pero no la de un prefijo de espacio de nombres. La consulta siguiente devuelve un error porque no se puede anular la declaración de un prefijo como se especifica en la construcción del elemento <b>.

declare @x xml  
set @x ='<x>5</x>'  
select @x.query( '  
  <x:a xmlns:x="a">  
    <b xmlns:x=""/>  
  </x:a>' )  

El espacio de nombres recientemente construido está disponible para su uso en la consulta. Por ejemplo, la consulta siguiente declara un espacio de nombres al construir el elemento , <FirstLocation>y especifica el prefijo en las expresiones para los valores de atributo LocationID y SetupHrs.

SELECT Instructions.query('  
        <FirstLocation xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"  
         LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"  
         SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >  
           { /AWMI:root/AWMI:Location[1]/AWMI:step }  
        </FirstLocation>   
') as Result   
FROM  Production.ProductModel  
where ProductModelID=7  

Tenga en cuenta que la creación de un nuevo prefijo de espacio de nombres de este modo reemplazará las declaraciones de espacios de nombres ya existentes de este prefijo. Por ejemplo, la declaración de espacio de nombres , AWMI="https://someURI", en el prólogo de consulta se invalida mediante la declaración de espacio de nombres en el <FirstLocation> elemento .

SELECT Instructions.query('  
declare namespace AWMI="https://someURI";  
        <FirstLocation xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"  
         LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"  
         SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >  
           { /AWMI:root/AWMI:Location[1]/AWMI:step }  
        </FirstLocation>   
') as Result   
FROM  Production.ProductModel  
where ProductModelID=7  

Utilizar un prólogo para agregar espacios de nombres

En este ejemplo se ilustra el modo en el que se pueden agregar espacios de nombres al XML construido. Se declara un espacio de nombres predeterminado en el prólogo de la consulta.

declare @x xml  
set @x ='<x>5</x>'  
select @x.query( '  
           declare default element namespace "a";  
            <a><b xmlns=""/></a>' )  

Tenga en cuenta que en la construcción del elemento <b>, el atributo de declaración de espacio de nombres se especifica con una cadena vacía como su valor. Esta acción anula la declaración del espacio de nombres predeterminado que se declara en el principal.

El resultado es el siguiente:

<a xmlns="a">  
  <b xmlns="" />  
</a>  

Construcción de XML y control de los espacios en blanco

El contenido de los elementos en la construcción de XML puede incluir caracteres de espacio en blanco. Estos caracteres se controlan de las formas siguientes:

  • Los caracteres de espacio en blanco de los URI del espacio de nombres se tratan como el tipo anyURI XSD. A continuación se especifica cómo se controlan:

    • Los caracteres de espacio en blanco situados al principio y al final se recortan.

    • Los caracteres de espacio en blanco internos se contraen en un solo espacio.

  • Los caracteres de avance de línea situados dentro del contenido de los atributos se sustituyen por espacios. Todos los demás caracteres de espacio en blanco permanecen tal cual están.

  • Los espacios en blanco dentro de los elementos se mantienen.

En el ejemplo siguiente se ilustra el control de los espacios en blanco en la construcción de XML:

-- line feed is repaced by space.  
declare @x xml  
set @x=''  
select @x.query('  
  
declare namespace myNS="   https://       
 abc/  
xyz  
  
";  
<test attr="    my   
test   attr   
value    " >  
  
<a>  
  
This     is  a  
  
test  
  
</a>  
</test>  
') as XML_Result  
  

El resultado es el siguiente:

-- result  
<test attr="<test attr="    my test   attr  value    "><a>  
  
This     is  a  
  
test  
  
</a></test>  
"><a>  
  
This     is  a  
  
test  
  
</a></test>  

Otros constructores directos de XML

Los constructores para el procesamiento de instrucciones y los comentarios XML utilizan la misma sintaxis que la de la construcción XML correspondiente. También se admiten los constructores calculados para nodos de texto, pero se utilizan básicamente en XML DML para construir nodos de texto.

Nota Para obtener un ejemplo del uso de un constructor de nodo de texto explícito, vea el ejemplo específico de inserción (XML DML).

En la consulta siguiente, el XML construido incluye un elemento, dos atributos, un comentario y una instrucción de procesamiento. Tenga en cuenta que se usa una coma antes de <FirstLocation>, porque se está construyendo una secuencia.

SELECT Instructions.query('  
  declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";  
   <?myProcessingInstr abc="value" ?>,   
   <FirstLocation   
        WorkCtrID = "{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"  
        SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >  
       <!-- some comment -->  
       <?myPI some processing instructions ?>  
       { (/AWMI:root/AWMI:Location[1]/AWMI:step) }  
    </FirstLocation>   
') as Result   
FROM Production.ProductModel  
where ProductModelID=7;  
  

Éste es el resultado parcial:

<?myProcessingInstr abc="value" ?>  
<FirstLocation WorkCtrID="10" SetupHrs="0.5">  
  <!-- some comment -->  
  <?myPI some processing instructions ?>  
  <AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">I  
  nsert <AWMI:material>aluminum sheet MS-2341</AWMI:material> into the <AWMI:tool>T-85A framing tool</AWMI:tool>.   
  </AWMI:step>  
    ...  
</FirstLocation>  
  

Utilizar constructores calculados

. En este caso, se especifican las palabras clave que identifican el tipo de nodo que se desea construir. Solo se admiten las palabras clave siguientes:

  • elemento

  • atributo

  • texto

En el caso de los nodos de elemento y atributo, estas palabras clave van seguidas del nombre del nodo y de la expresión, entre llaves, que genera el contenido del nodo. En el ejemplo siguiente, se construye este XML:

<root>  
  <ProductModel PID="5">Some text <summary>Some Summary</summary></ProductModel>  
</root>  

Ésta es la consulta que utiliza constructores calculados para generar el XML:

declare @x xml  
set @x=''  
select @x.query('element root   
               {   
                  element ProductModel  
     {  
attribute PID { 5 },  
text{"Some text "},  
    element summary { "Some Summary" }  
 }  
               } ')  
  

La expresión que genera el contenido del nodo puede especificar una expresión de consulta.

declare @x xml  
set @x='<a attr="5"><b>some summary</b></a>'  
select @x.query('element root   
               {   
                  element ProductModel  
     {  
attribute PID { /a/@attr },  
text{"Some text "},  
    element summary { /a/b }  
 }  
               } ')  

Tenga en cuenta que los constructores de elementos y atributos calculados, tal como se define en la especificación de XQuery, permiten calcular los nombres de los nodos. Cuando se usan constructores directos en SQL Server, los nombres de nodo, como el elemento y el atributo, deben especificarse como literales constantes. Por tanto, no existe ninguna diferencia entre los constructores directos y los calculados para los elementos y atributos.

En el ejemplo siguiente, el contenido de los nodos construidos se obtiene de las instrucciones de fabricación XML almacenadas en la columna Instructions del tipo de datos xml de la tabla ProductModel.

SELECT Instructions.query('  
  declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";  
   element FirstLocation   
     {  
        attribute LocationID { (/AWMI:root/AWMI:Location[1]/@LocationID)[1] },  
        element   AllTheSteps { /AWMI:root/AWMI:Location[1]/AWMI:step }  
     }  
') as Result   
FROM  Production.ProductModel  
where ProductModelID=7  

Éste es el resultado parcial:

<FirstLocation LocationID="10">  
  <AllTheSteps>  
    <AWMI:step> ... </AWMI:step>  
    <AWMI:step> ... </AWMI:step>  
    ...  
  </AllTheSteps>  
</FirstLocation>    

Limitaciones adicionales de la implementación

No se pueden utilizar constructores de atributos calculados para declarar un nuevo espacio de nombres. Además, no se admiten los siguientes constructores calculados en SQL Server:

  • Constructores de nodos de documentos calculados

  • Constructores de instrucciones de procesamiento calculados

  • Constructores de comentarios calculados

Consulte también

Expresiones XQuery