Usar el modo EXPLICIT

Como se describe en el tema Generar XML mediante FOR XML, los modos RAW y AUTO no proporcionan demasiado control sobre la forma del XML generado a partir del resultado de una consulta. Sin embargo, el modo EXPLICIT ofrece la máxima flexibilidad para generar el XML que se desee a partir del resultado de una consulta.

La consulta en modo EXPLICIT debe escribirse de una determinada manera para poder especificar explícitamente la información adicional sobre el XML requerido, como el anidamiento esperado en el XML, como parte de la propia consulta. Dependiendo del XML que se solicite, la escritura de consultas en modo EXPLICIT puede resultar complicada. Tal vez, una alternativa más sencilla que escribir consultas en modo EXPLICIT sea usar el modo PATH con anidamiento.

En modo EXPLICIT, el XML que se desea se describe como parte de la consulta, por lo que es necesario asegurarse de que el XML generado sea correcto y válido.

Procesar conjuntos de filas en modo EXPLICIT

El modo EXPLICIT transforma en un documento XML el conjunto de filas resultante de la ejecución de la consulta. Para que el modo EXPLICIT pueda generar el documento XML, el conjunto de filas debe ajustarse a un determinado formato. Por ello, es necesario escribir la consulta SELECT para gemerar el conjunto de filas, la tabla universal, con un formato específico que permita a la lógica del procesamiento generar el XML deseado.

En primer lugar, la consulta debe crear las dos columnas de metadatos siguientes:

  • La primera columna debe proporcionar el número de etiqueta, el tipo de entero, del elemento actual, y el nombre de la columna debe ser Tag. La consulta debe proporcionar un número de etiqueta único para cada elemento que se vaya a construir a partir del conjunto de filas.
  • La segunda columna debe proporcionar un número de etiqueta del elemento primario, y el nombre de la columna debe ser Parent. De este modo, las columnas Tag y Parent ofrecen información sobre la jerarquía.

Los valores de estas columnas de metadatos, junto con la información de los nombres de columna, se usan para generar el XML deseado. Tenga en cuenta que la consulta debe proporcionar los nombres de columna de una manera determinada. Observe también que un valor 0 ó NULL en la columna Parent indica que el elemento correspondiente no tiene uno primario. El elemento se agrega al XML como elemento de nivel superior.

Para comprender cómo se procesa la tabla universal generada por una consulta para obtener el XML resultante, suponga que ha escrito una consulta que genera esta tabla universal:

Ejemplo de tabla universal

Observe lo siguiente en esta tabla universal:

  • Las dos primeras columnas son Tag y Parent y son de metadatos. Estos valores determinan la jerarquía.
  • Los nombres de columna se han especificado de una manera determinada, como se describe más adelante en este tema
  • Al generar el XML a partir de esta tabla universal, se crea una partición vertical de los datos de la tabla en grupos de columnas. La agrupación se determina en función del valor de Tag y los nombres de columna. Al crear XML, la lógica de procesamiento selecciona un grupo de columnas para cada fila y construye un elemento. En este ejemplo, se observa lo siguiente:
    • Para el valor 1 de la columna Tag en la primera fila, las columnas cuyos nombres incluyen el mismo número de etiqueta, Customer!1!cid y Customer!1!name, forman un grupo. Estas columnas se utilizan para procesar la fila, y se observa que la forma del elemento generado es <Customer id=... name=...>. El formato del nombre de columna se describe más adelante en este tema.
    • Para las filas con valor 2 en la columna Tag, las columnas Order!2!id y Order!2!date forman un grupo que se usa después para construir elementos, <Order id=... date=... />.
    • Para las filas con valor 3 en la columna Tag, las columnas OrderDetail!3!id!id y OrderDetail!3!pid!idref forman un grupo. Cada una de estas filas genera un elemento, <OrderDetail id=... pid=...>, a partir de estas columnas.
  • Tenga en cuenta que, al generar la jerarquía en XML, las filas se procesan por orden. La jerarquía XML se determina como se indica a continuación:
    • La primera fila especifica el valor 1 en Tag y el valor NULL en Parent. Por lo tanto, el elemento correspondiente, <Customer>, se agrega al XML como elemento de nivel superior.

      <Customer cid="C1" name="Janine">
      
    • La segunda fila identifica el valor 2 en Tag y el valor 1 en Parent. Por lo tanto, el elemento, <Order>, se agrega como elemento secundario de <Customer>.

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
      
    • Las dos filas siguientes identifican el valor 3 en Tag y el valor 2 en Parent. Por lo tanto, los dos elementos <OrderDetail> se agregan como elementos secundarios de <Order>.

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
            <OrderDetail id="OD1" pid="P1"/>
            <OrderDetail id="OD2" pid="P2"/>
      
    • La última fila identifica 2 como número de etiqueta en Tag y 1 como número de etiqueta en Parent. Por lo tanto, se agrega otro elemento secundario <Order> al elemento primario <Customer>.

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
            <OrderDetail id="OD1" pid="P1"/>
            <OrderDetail id="OD2" pid="P2"/>
         </Order>
         <Order id="O2" date="3/29/1997">
      </Customer>
      

En resumen, los valores de las columnas de metadatos Tag y Parent, la información proporcionada en los nombres de columna y el orden correcto de las filas generan el XML deseado al utilizar el modo EXPLICIT.

Ordenación de las filas en la tabla universal

Al generar el XML, las filas de la tabla universal se procesan por orden. Por tanto, para recuperar las instancias secundarias correctas asociadas a su instancia primaria, las filas del conjunto de filas deben estar ordenadas de modo que a cada nodo principal le sigan directamente sus nodos secundarios.

Especificar nombres de columna en una tabla universal

Al escribir consultas en modo EXPLICIT, los nombres de columna del conjunto de filas resultante se deben especificar con este formato. Ofrecen información de transformación, incluidos nombres de elementos y atributos y otros datos, especificada mediante el uso de directivas.

Éste es el formato general:

ElementName!TagNumber!AttributeName!Directive

A continuación, se describe cada parte del formato.

  • ElementName
    Es el identificador genérico resultante del elemento. Por ejemplo, si se especifica Customers como ElementName, se genera el elemento <Customers>.
  • TagNumber
    Es un valor de etiqueta único asignado a un elemento. Este valor, junto con las dos columnas de metadatos Tag y Parent, determina el anidamiento de los elementos en el XML resultante.
  • AttributeName
    Proporciona el nombre del atributo que se va a crear en el identificador ElementName especificado. Éste es el comportamiento si no se especifica Directive.

    Si se especifica Directive y es xml, cdata o element, este valor se utiliza para crear un elemento secundario de ElementName, y se le agrega el valor de la columna.

    Si se especifica Directive, AttributeName puede estar vacío. Por ejemplo, ElementName!TagNumber!!Directive. En este caso, el valor de la columna está incluido directamente en el ElementName.

  • Directive
    Directive es opcional y se puede usar para proporcionar información adicional para generar el XML. Directive tiene dos objetivos.

    Uno de los objetivos es codificar valores como ID, IDREF e IDREFS. Puede especificar palabras clave ID, IDREF e IDREFS como valores de Directive. Estas directivas sobrescriben los tipos de atributo. De este modo, puede crear vínculos entre documentos.

    También puede usar Directive para indicar la forma de asignar los datos de cadena a XML. Las palabras clave hide, element, elementxsinil, xml, xmltext y cdata se pueden usar como Directive. La directiva hide oculta el nodo. Es útil cuando se recuperan valores sólo para ordenarlos, sin incluirlos en el XML resultante.

    La directiva element genera un elemento contenido en lugar de un atributo. Los datos contenidos se codifican como entidad. Por ejemplo, el carácter < pasa a ser &lt;. En el caso de valores de columna NULL, no se genera ningún elemento. Si desea que se genere un elemento para valores de columna NULL, puede especificar la directiva elementxsinil. De este modo, se generará un elemento con el atributo sxi:nil=TRUE.

    La directiva xml es la misma que la directiva element, excepto en que no se produce la codificación de entidades. Observe que la directiva element se puede combinar con ID, IDREF o IDREFS, mientras que la directiva xml no se puede combinar con ninguna otra directiva, excepto hide.

    La directiva cdata Incluye los datos englobándolos en una sección CDATA. El contenido no se codifica por entidad. El tipo de datos original debe ser de texto, como varchar, nvarchar, text o ntext. Esta directiva sólo puede utilizarse con hide. Si se utiliza esta directiva, no debe especificarse AttributeName.

    La combinación de directivas entre estos dos grupos es válida en la mayoría de los casos, pero no se permite la combinación entre ellas mismas.

    Si no se especifican Directive ni AttributeName (por ejemplo, Customer!1), se implica una directiva element (como Customer!1!!element) y los datos de columna se incluyen en ElementName.

    Si se especifica la directiva xmltext, el contenido de la columna se incluye en una única etiqueta que se integrará con el resto del documento. Esta directiva es útil para recuperar los datos XML de desbordamiento no utilizados que OPENXML almacena en una columna. Para obtener más información, vea Consultar XML con OPENXML.

    Si se especifica AttributeName, el nombre de etiqueta se sustituye por el nombre especificado. En caso contrario, el atributo se agrega a la lista actual de atributos de los elementos que los incluyen colocando el contenido al principio del contenido sin codificación de entidades. La columna con esta directiva debe ser de tipo texto, como varchar, nvarchar, char, nchar, text o ntext. Esta directiva sólo puede utilizarse con hide. Esta directiva es útil para recuperar los datos de desbordamiento almacenados en una columna. Si el contexto no es un XML bien estructurado, el comportamiento no está definido.

Ejemplos

Los siguientes ejemplos ilustran el uso del modo EXPLICIT.

A. Recuperar información de los empleados

En este ejemplo, se recupera el Id. y el nombre de cada empleado. En la base de datos AdventureWorks, el Id. de empleado se puede obtener de la tabla Employee. Los nombres de los empleados se pueden obtener de la tabla Contact. Para combinar las tablas, se puede usar la columna ContactID.

Suponga que desea una transformación FOR XML EXPLICIT para generar XML como se indica a continuación:

<Employee EmpID="1" >
  <Name FName="Guy" LName="Gilbert" />
</Employee>
...

Dado que hay dos niveles en la jerarquía, habría que escribir dos consultas SELECT y aplicar UNION ALL. Ésta es la primera consulta, que recupera los valores correspondientes al elemento <Employee> y sus atributos. La consulta asigna 1 como valor de Tag para el elemento <Employee> y NULL como Parent, puesto que se trata del elemento de nivel superior.

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName],
       NULL       as [Name!2!LName]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID

Ésta es la segunda consulta. Recupera los valores correspondientes al elemento <Name>. Asigna 2 como valor de Tag para el elemento <Name> y 1 como valor de etiqueta en Parent, para identificar <Employee> como elemento primario.

SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID

Estas consultas se combinan con UNION ALL, se aplica FOR XML EXPLICIT y se especifica la cláusula ORDER BY requerida. El conjunto de filas se debe ordenar primero según el valor de EmployeeID y, a continuación, por nombre, de modo que los valores NULL en el nombre aparezcan al principio. Si ejecuta la siguiente consulta sin la cláusula FOR XML, podrá ver la tabla universal generada.

Ésta es la consulta final:

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName],
       NULL       as [Name!2!LName]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
ORDER BY [Employee!1!EmpID],[Name!2!FName]
FOR XML EXPLICIT

Éste es el resultado parcial:

<Employee EmpID="1">
  <Name FName="Guy" LName="Gilbert" />
</Employee>
<Employee EmpID="2">
  <Name FName="Kevin" LName="Brown" />
</Employee>
...

El primer SELECT especifica nombres para columnas del conjunto de filas resultante. Estos nombres forman dos grupos de columnas. El grupo con el valor 1 de Tag en el nombre de columna identifica Employee como elemento y EmpID como atributo. El otro grupo de columnas tiene el valor 2 de Tag e identifica <Name> como elemento, y FName y LName como atributos.

La siguiente tabla muestra el conjunto de filas parcial generado por la consulta:

Tag Parent  Employee!1!EmpID Name!2!FName Name!2!LName
----------- ----------- ---------------- -------------------
1    NULL     1                NULL          NULL
2     1       1                Guy           Gilbert
1    NULL     2                NULL          NULL
2     1       2                Kevin         Brown
1    NULL     3                NULL          NULL
2     1       3                Roberto       Tamburello 
...

Así es como se procesan las filas de la tabla universal para crear el árbol XML resultante:

La primera fila identifica el valor 1 para Tag. Por lo tanto, se identifica el grupo de columnas con valor 1 en Tag, Employee!1!EmpID. Esta columna identifica Employee como nombre de elemento. A continuación, se crea un elemento <Employee> con atributos EmpID. Los valores de columna correspondientes se asignan a estos atributos.

La segunda fila tiene el valor 2 para Tag. Por lo tanto, se identifica el grupo de columnas con valor 2 de Tag en el nombre de columna, Name!2!FName, Name!2!LName. Estos nombres de columna identifican Name como nombre de elemento. Se crea un elemento <Name> con atributos FName y LName. A continuación, se asignan los valores de columna correspondientes a estos atributos. Esta fila identifica 1 como valor de Parent. Este elemento secundario se agrega al elemento <Employee> anterior.

Este proceso se repite con el resto de filas del conjunto. Observe la importancia de ordenar las filas de la tabla universal para que FOR XML EXPLICIT pueda procesar el conjunto de filas por orden y generar el XML deseado.

B. Especificar la directiva element

Este ejemplo es similar al ejemplo A, con la excepción de que ahora se genera XML centrado en elementos, como se indica a continuación:

<Employee EmpID=...>
  <Name>
    <FName>...</FName>
    <LName>...</LName>
  </Name>
</Employee>

La consulta sólo cambia en que se agrega la directiva element en los nombres de columna. Por lo tanto, en lugar de atributos, se agregan los elementos secundarios <FName> y <LName> al elemento <Name>. Dado que la columna Employee!1!EmpID no especifica la directiva ELEMENT, EmpID se agrega como atributo del elemento <Employee>.

SELECT 1 as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName!ELEMENT],
       NULL       as [Name!2!LName!ELEMENT]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
ORDER BY [Employee!1!EmpID],[Name!2!FName!ELEMENT]
FOR XML EXPLICIT

El resultado parcial es el siguiente.

<Employee EmpID="1">
  <Name>
    <FName>Guy</FName>
    <LName>Gilbert</LName>
  </Name>
</Employee>
<Employee EmpID="2">
  <Name>
    <FName>Kevin</FName>
    <LName>Brown</LName>
  </Name>
</Employee>
...

C. Especificar la directiva elementxsinil

Cuando se especifica la directiva element para recuperar XML centrado en elementos, si la columna tiene algún valor NULL, el modo EXPLICIT no genera el elemento correspondiente. Opcionalmente, se puede especificar la directiva elementxsinil para solicitar al elemento que se genera valores NULL donde el atributo xsi:nil esté establecido con el valor TRUE.

La consulta siguiente genera XML que incluye una dirección del empleado. En el caso de las columnas AddressLine2 y City, los nombres de columna especifican la directiva elementxsinil. Así, se genera el elemento para valores NULL en las columnas AddressLine2 y City del conjunto de filas.

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID  as [Employee!1!EmpID],
       E.AddressID as [Employee!1!AddressID],
       NULL        as [Address!2!AddressID],
       NULL        as [Address!2!AddressLine1!ELEMENT],
       NULL        as [Address!2!AddressLine2!ELEMENTXSINIL],
       NULL        as [Address!2!City!ELEMENTXSINIL]
FROM   HumanResources.EmployeeAddress E, Person.Address A
WHERE  E.ContactID = A.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       E.AddressID,
       A.AddressID,
       AddressLine1, 
       AddressLine2,
       City 
FROM   HumanResources.EmployeeAddress E, Person.Address A
WHERE  E.AddressID = A.AddressID
ORDER BY [Employee!1!EmpID],[Address!2!AddressID]
FOR XML EXPLICIT

Éste es el resultado parcial:

<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        EmpID="1" AddressID="61">
  <Address AddressID="61">
    <AddressLine1>7726 Driftwood Drive</AddressLine1>
    <AddressLine2 xsi:nil="true" />
    <City>Monroe</City>
  </Address>
</Employee>
...

D. Construir elementos del mismo nivel mediante el modo EXPLICIT

Suponga que desea crear XML para obtener información sobre pedidos. Observe que los elementos <SalesPerson> y <OrderDetail> son del mismo nivel. Cada pedido tiene un elemento <OrderHeader>, un elemento <SalesPerson> y uno o más elementos <OrderDetail>.

<OrderHeader SalesOrderID=... OrderDate=... CustomerID=... >
  <SalesPerson SalesPersonID=... />
  <OrderDetail SalesOrderID=... LineTotal=... ProductID=... OrderQty=... />
  <OrderDetail SalesOrderID=... LineTotal=... ProductID=... OrderQty=.../>
      ...
</OrderHeader>
<OrderHeader ...</OrderHeader>

La siguiente consulta en modo EXPLICIT crea este XML. Observe que la consulta especifica valores 1 en Tag para el elemento <OrderHeader>, 2 para el elemento <SalesPerson> y 3 para el elemento <OrderDetail>. Dado que <SalesPerson> y <OrderDetail> son del mismo nivel, la consulta especifica el mismo valor de etiqueta 1 en Parent, que identifica el elemento <OrderHeader>.

SELECT  1 as Tag,
        0 as Parent,
        SalesOrderID  as [OrderHeader!1!SalesOrderID],
        OrderDate     as [OrderHeader!1!OrderDate],
        CustomerID    as [OrderHeader!1!CustomerID],
        NULL          as [SalesPerson!2!SalesPersonID],
        NULL          as [OrderDetail!3!SalesOrderID],
        NULL          as [OrderDetail!3!LineTotal],
        NULL          as [OrderDetail!3!ProductID],
        NULL          as [OrderDetail!3!OrderQty]
FROM   Sales.SalesOrderHeader
WHERE     SalesOrderID=43659 or SalesOrderID=43661
UNION ALL 
SELECT 2 as Tag,
       1 as Parent,
        SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,  
        NULL,         
        NULL,         
        NULL,
        NULL         
FROM   Sales.SalesOrderHeader
WHERE     SalesOrderID=43659 or SalesOrderID=43661
UNION ALL
SELECT 3 as Tag,
       1 as Parent,
        SOD.SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,
        SOH.SalesOrderID,
        LineTotal,
        ProductID,
        OrderQty   
FROM    Sales.SalesOrderHeader SOH,Sales.SalesOrderDetail SOD
WHERE   SOH.SalesOrderID = SOD.SalesOrderID
AND     (SOH.SalesOrderID=43659 or SOH.SalesOrderID=43661)
ORDER BY [OrderHeader!1!SalesOrderID], [SalesPerson!2!SalesPersonID],
         [OrderDetail!3!SalesOrderID],[OrderDetail!3!LineTotal]
FOR XML EXPLICIT

Éste es el resultado parcial:

<OrderHeader SalesOrderID="43659" OrderDate="2001-07-01T00:00:00" CustomerID="676">
  <SalesPerson SalesPersonID="279" />
  <OrderDetail SalesOrderID="43659" LineTotal="10.373000" ProductID="712" OrderQty="2" />
  <OrderDetail SalesOrderID="43659" LineTotal="28.840400" ProductID="716" OrderQty="1" />
  <OrderDetail SalesOrderID="43659" LineTotal="34.200000" ProductID="709" OrderQty="6" />
   ...
</OrderHeader>
<OrderHeader SalesOrderID="43661" OrderDate="2001-07-01T00:00:00" CustomerID="442">
  <SalesPerson SalesPersonID="282" />
  <OrderDetail SalesOrderID="43661" LineTotal="20.746000" ProductID="712" OrderQty="4" />
  <OrderDetail SalesOrderID="43661" LineTotal="40.373000" ProductID="711" OrderQty="2" />
   ...
</OrderHeader>

E. Especificar las directivas ID e IDREF

Este ejemplo es similar al ejemplo C. La única diferencia es que la consulta especifica las directivas ID e IDREF. Estas directivas sobrescriben los tipos del atributo SalesPersonID en los elementos <OrderHeader> y <OrderDetail>. De este modo, se forman vínculos entre documentos. Para ver los tipos sobrescritos, se necesita el esquema. Por lo tanto, la consulta especifica la opción XMLDATA en la cláusula FOR XML para recuperar el esquema.

SELECT  1 as Tag,
        0 as Parent,
        SalesOrderID  as [OrderHeader!1!SalesOrderID!id],
        OrderDate     as [OrderHeader!1!OrderDate],
        CustomerID    as [OrderHeader!1!CustomerID],
        NULL          as [SalesPerson!2!SalesPersonID],
        NULL          as [OrderDetail!3!SalesOrderID!idref],
        NULL          as [OrderDetail!3!LineTotal],
        NULL          as [OrderDetail!3!ProductID],
        NULL          as [OrderDetail!3!OrderQty]
FROM   Sales.SalesOrderHeader
WHERE  SalesOrderID=43659 or SalesOrderID=43661
UNION ALL 
SELECT 2 as Tag,
       1 as Parent,
        SalesOrderID, 
        NULL,
        NULL,
        SalesPersonID,  
        NULL,         
        NULL,         
        NULL,
        NULL         
FROM   Sales.SalesOrderHeader
WHERE  SalesOrderID=43659 or SalesOrderID=43661
UNION ALL
SELECT 3 as Tag,
       1 as Parent,
        SOD.SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,
        SOH.SalesOrderID,
        LineTotal,
        ProductID,
        OrderQty   
FROM    Sales.SalesOrderHeader SOH,Sales.SalesOrderDetail SOD
WHERE   SOH.SalesOrderID = SOD.SalesOrderID
AND     (SOH.SalesOrderID=43659 or SOH.SalesOrderID=43661)
ORDER BY [OrderHeader!1!SalesOrderID!id], [SalesPerson!2!SalesPersonID],
         [OrderDetail!3!SalesOrderID!idref],[OrderDetail!3!LineTotal]
FOR XML EXPLICIT, XMLDATA

El resultado parcial es el siguiente. En el esquema, observe que las directivas ID e IDREF han sobrescrito los tipos de datos del atributo SalesOrderID en los elementos <OrderHeader> y <OrderDetail>. Si quita estas directivas, el esquema devuelve tipos originales de estos atributos.

<Schema name="Schema1" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="OrderHeader" content="mixed" model="open">
    <AttributeType name="SalesOrderID" dt:type="id" />
    <AttributeType name="OrderDate" dt:type="dateTime" />
    <AttributeType name="CustomerID" dt:type="i4" />
    <attribute type="SalesOrderID" />
    <attribute type="OrderDate" />
    <attribute type="CustomerID" />
  </ElementType>
  <ElementType name="SalesPerson" content="mixed" model="open">
    <AttributeType name="SalesPersonID" dt:type="i4" />
    <attribute type="SalesPersonID" />
  </ElementType>
  <ElementType name="OrderDetail" content="mixed" model="open">
    <AttributeType name="SalesOrderID" dt:type="idref" />
    <AttributeType name="LineTotal" dt:type="number" />
    <AttributeType name="ProductID" dt:type="i4" />
    <AttributeType name="OrderQty" dt:type="i2" />
    <attribute type="SalesOrderID" />
    <attribute type="LineTotal" />
    <attribute type="ProductID" />
    <attribute type="OrderQty" />
  </ElementType>
</Schema>
<OrderHeader xmlns="x-schema:#Schema1" SalesOrderID="43659" OrderDate="2001-07-01T00:00:00" CustomerID="676">
  <SalesPerson SalesPersonID="279" />
  <OrderDetail SalesOrderID="43659" LineTotal="10.373000" ProductID="712" OrderQty="2" />
  ...
</OrderHeader>
...

F. Especificar las directivas ID e IDREFS

El atributo de un elemento se puede especificar como atributo de tipo ID y el atributo IDREFS puede usarse entonces para hacer referencia a él. Así, se habilitan vínculos entre documentos; esto es similar a las relaciones entre clave principal y clave externa en bases de datos relacionales.

Este ejemplo ilustra cómo se pueden usar las directivas ID e IDREFS para crear atributos de tipos ID e IDREFS. Dado que los identificadores no pueden contener valores enteros, los valores ID de este ejemplo se convierten. Dicho de otro modo, se realiza una conversión de tipos. Para los valores ID se utilizan prefijos.

Suponga que desea crear XML como se indica a continuación:

<Customer CustomerID="C1" SalesOrderIDList=" O11 O22 O33..." >
    <SalesOrder SalesOrderID="O11" OrderDate="..." />
    <SalesOrder SalesOrderID="O22" OrderDate="..." />
    <SalesOrder SalesOrderID="O33" OrderDate="..." />
    ...
</Customer>

El atributo SalesOrderIDList del elemento <Customer> es un atributo con varios valores que hace referencia al atributo SalesOrderID del elemento <SalesOrder>. Para establecer este vínculo, el atributo SalesOrderID debe declararse de tipo ID y el atributo SalesOrderIDList del elemento <Customer> debe declararse de tipo IDREFS. Dado que un cliente puede solicitar varios pedidos, se utiliza el tipo IDREFS.

IDREFS también tiene más de un valor. Por lo tanto, se tiene que utilizar una cláusula SELECT por separado, que volverá a utilizar la misma información de columna de clave, etiqueta y elemento primario. A continuación, ORDER BY tiene que asegurarse de que la secuencia de filas que componen los valores de IDREFS aparezca agrupada bajo el elemento primario.

Esta es la consulta que genera el XML deseado. La consulta utiliza las directivas ID e IDREFS para sobrescribir los tipos de los nombres de columna (SalesOrder!2!SalesOrderID!ID, Customer!1!SalesOrderIDList!IDREFS).

SELECT  1 as Tag,
        0 as Parent,
        C.CustomerID       [Customer!1!CustomerID],
        NULL               [Customer!1!SalesOrderIDList!IDREFS],
        NULL               [SalesOrder!2!SalesOrderID!ID],
        NULL               [SalesOrder!2!OrderDate]
FROM   Sales.Customer C 
UNION ALL 
SELECT  1 as Tag,
        0 as Parent,
        C.CustomerID,
        'O-'+CAST(SalesOrderID as varchar(10)), 
        NULL,
        NULL
FROM   Sales.Customer C, Sales.SalesOrderHeader SOH
WHERE  C.CustomerID = SOH.CustomerID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
        C.CustomerID,
        NULL,
        'O-'+CAST(SalesOrderID as varchar(10)),
        OrderDate
FROM   Sales.Customer C, Sales.SalesOrderHeader SOH
WHERE  C.CustomerID = SOH.CustomerID
ORDER BY [Customer!1!CustomerID] ,
         [SalesOrder!2!SalesOrderID!ID],
         [Customer!1!SalesOrderIDList!IDREFS]
FOR XML EXPLICIT

G. Utilizar la directiva hide para ocultar elementos y atributos del XML resultante

Este ejemplo muestra el uso de la directiva hide. Esta directiva es útil si desea que la consulta devuelva un atributo para ordenar las filas de la tabla universal devuelta por la consulta, pero no desea que ese atributo aparezca en el documento XML resultante.

Esta consulta genera este XML:

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
           <Summary> element from XML stored in CatalogDescription column
    </SummaryDescription>
  </Summary>
</ProductModel>

Esta consulta genera el XML deseado. La consulta identifica dos grupos de columnas con valores 1 y 2 de Tag en los nombres de columna.

Esta consulta utiliza el método query() del tipo de datos XML del tipo de datos xml para consultar la columna CatalogDescription de tipo xml y recuperar la descripción resumida. Esta consulta también utiliza el método value() del tipo de datos xml para recuperar el valor ProductModelID de la columna CatalogDescription. Este valor no se requiere en el XML resultante, pero sí es necesario para ordenar el conjunto de filas resultante. Por lo tanto, el nombre de columna [Summary!2!ProductModelID!hide] incluye la directiva hide. Si no se incluye esta columna en la instrucción SELECT, habrá que ordenar el conjunto de filas según [ProductModel!1!ProdModelID] y [Summary!2!SummaryDescription] que es de tipo xml y no se podrá usar la columna de tipo xml en ORDER BY. Por tanto, se agrega la columna adicional [Summary!2!ProductModelID!hide] y luego se especifica en la cláusula ORDER BY.

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID     as [ProductModel!1!ProdModelID],
        Name               as [ProductModel!1!Name],
        NULL               as [Summary!2!ProductModelID!hide],
        NULL               as [Summary!2!SummaryDescription]
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        Name,
        CatalogDescription.value('
         declare namespace PD="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
       (/PD:ProductDescription/@ProductModelID)[1]', 'int'),
        CatalogDescription.query('
         declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
         /pd:ProductDescription/pd:Summary')
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
ORDER BY [ProductModel!1!ProdModelID],[Summary!2!ProductModelID!hide]
FOR XML EXPLICIT
go

El resultado es el siguiente:

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
      <pd:Summary xmlns:pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription" >
        <p1:p xmlns:p1="http://www.w3.org/1999/xhtml">Our top-of-the-line competition mountain bike. Performance-enhancing options include the innovative HL Frame, super-smooth front suspension, and traction for all terrain. </p1:p>
      </pd:Summary>
    </SummaryDescription>
  </Summary>
</ProductModel>

H. Especificar la directiva element y la codificación de entidades

Este ejemplo muestra la diferencia entre las directivas element y xml. La directiva element crea entidades para los datos, pero la directiva xml no lo hace. Al elemento <Summary> se le asigna XML, <Summary>This is summary description</Summary>, en la consulta.

Considere esta consulta:

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        NULL            as [Summary!2!SummaryDescription!ELEMENT]
FROM    Production.ProductModel
WHERE   ProductModelID=19
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        NULL,
       '<Summary>This is summary description</Summary>'
FROM   Production.ProductModel
WHERE  ProductModelID=19
FOR XML EXPLICIT

Éste es el resultado. Se crea una entidad en el resultado para la descripción resumida.

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>&lt;Summary&gt;This is summary description&lt;/Summary&gt;</SummaryDescription>
  </Summary>
</ProductModel>

Ahora, si se especifica la directiva xml en el nombre de columna Summary!2!SummaryDescription!xml, en lugar de la directiva element, se recibirá la descripción resumida sin la creación de entidades.

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
      <Summary>This is summary description</Summary>
    </SummaryDescription>
  </Summary>
</ProductModel>

En lugar de asignar un valor XML estático, la consulta siguiente utiliza el método query() del tipo xml para recuperar la descripción resumida del modelo de productos de la columna CatalogDescription de tipo xml. Como se sabe que el resultado es de tipo xml, no se aplica la creación de entidades.

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        NULL            as [Summary!2!SummaryDescription]
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        Name,
       (SELECT CatalogDescription.query('
            declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
          /pd:ProductDescription/pd:Summary'))
FROM     Production.ProductModel
WHERE    CatalogDescription is not null
ORDER BY [ProductModel!1!ProdModelID],Tag
FOR XML EXPLICIT

I. Especificar la directiva cdata

Si se establece la directiva en cdata, los datos contenidos no se codifican por entidad, pero se colocan en la sección CDATA. Los atributos cdata no deben tener nombre.

La siguiente consulta engloba la descripción resumida de modelos de productos en una sección CDATA.

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        '<Summary>This is summary description</Summary>'   
            as [ProductModel!1!!cdata] -- no attribute name so ELEMENT assumed
FROM    Production.ProductModel
WHERE   ProductModelID=19
FOR XML EXPLICIT

El resultado es el siguiente:

<ProductModel ProdModelID="19" Name="Mountain-100">
   <![CDATA[<Summary>This is summary description</Summary>]]>
</ProductModel>

J. Especificar la directiva xmltext

Este ejemplo muestra cómo se tratan los datos de la columna de desbordamiento mediante la directiva xmltext en una instrucción SELECT con el modo EXPLICIT.

Considere la tabla Person. Esta tabla dispone de una columna Overflow que almacena la parte no utilizada del documento XML.

CREATE TABLE Person(PersonID varchar(5), PersonName varchar(20), Overflow nvarchar(200))
INSERT INTO Person VALUES ('P1','Joe',N'<SomeTag attr1="data">content</SomeTag>')
INSERT INTO Person VALUES ('P2','Joe',N'<SomeTag attr2="data"/>')
INSERT INTO Person VALUES ('P3','Joe',N'<SomeTag attr3="data" PersonID="P">content</SomeTag>')

Esta consulta recupera columnas de la tabla Person. En la columna Overflow, no se especifica AttributeName, sino que Directive se establece en xmltext como parte del proceso destinado a proporcionar un nombre de columna de la tabla universal.

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!!xmltext] -- No AttributeName; xmltext directive
FROM Person
FOR XML EXPLICIT

En el documento XML resultante:

  • Puesto que no se especifica AttributeName para la columna Overflow y se especifica la directiva xmltext, los atributos del elemento <overflow> se anexan a la lista de atributos del elemento <Parent> que los incluye.
  • Dado que el atributo PersonID del elemento <xmltext> está en conflicto con el atributo PersonID recuperado en el mismo nivel de elementos, se omite el atributo del elemento <xmltext>, incluso aunque PersonID sea NULL. En general, un atributo prevalece sobre un atributo del mismo nombre en el desbordamiento.

El resultado es el siguiente:

<Parent PersonID="P1" PersonName="Joe" attr1="data">content</Parent>
<Parent PersonID="P2" PersonName="Joe" attr2="data"></Parent>
<Parent PersonID="P3" PersonName="Joe" attr3="data">content</Parent>

Si los datos de desbordamiento contienen subelementos y se especifica la misma consulta, se agregan los subelementos en la columna Overflow como subelementos del elemento <Parent> que los incluye.

Por ejemplo, puede cambiar los datos de la tabla Person para que la columna Overflow disponga ahora de subelementos.

TRUNCATE TABLE Person
INSERT INTO Person VALUES ('P1','Joe',N'<SomeTag attr1="data">content</SomeTag>')
INSERT INTO Person VALUES ('P2','Joe',N'<SomeTag attr2="data"/>')
INSERT INTO Person VALUES ('P3','Joe',N'<SomeTag attr3="data" PersonID="P"><name>PersonName</name></SomeTag>')

Si se ejecuta la misma consulta, se agregan los subelementos del elemento <xmltext> como subelementos del elemento <Parent> que los incluye.

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!!xmltext] -- no AttributeName, xmltext directive
FROM Person
FOR XML EXPLICIT

El resultado es el siguiente:

<Parent PersonID="P1" PersonName="Joe" attr1="data">content</Parent>
<Parent PersonID="P2" PersonName="Joe" attr2="data"></Parent>
<Parent PersonID="P3" PersonName="Joe" attr3="data">
  <name>PersonName</name>
</Parent>

Si se especifica AttributeName con la directiva xmltext, se agregan los atributos del elemento <overflow> como atributos de los subelementos del elemento <Parent> que los incluye. El nombre especificado para AttributeName se convierte en el nombre del subelemento.

En esta consulta, se especifica AttributeName (<overflow>) junto con la directiva xmltext**:

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!overflow!xmltext] -- overflow is AttributeName
                      -- xmltext is directive
FROM Person
FOR XML EXPLICIT

El resultado es el siguiente:

<Parent PersonID="P1" PersonName="Joe">
  <overflow attr1="data">content</overflow>
</Parent>
<Parent PersonID="P2" PersonName="Joe">
  <overflow attr2="data" />
</Parent>
<Parent PersonID="P3" PersonName="Joe">
  <overflow attr3="data" PersonID="P">
    <name>PersonName</name>
  </overflow>
</Parent>

En este elemento de consulta, se especifica Directive en el atributo PersonName. De este modo, se agrega PersonName como subelemento del elemento <Parent> que lo incluye. Los atributos de <xmltext> se siguen agregando al elemento <Parent> que lo incluye. El contenido del elemento <overflow> (subelementos) se antepone a otros subelementos de los elementos <Parent> que lo incluyen.

SELECT 1      as Tag, NULL as parent,
       PersonID   as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName!element], -- element directive
       overflow   as [Parent!1!!xmltext]
FROM Person
FOR XML EXPLICIT

El resultado es el siguiente:

<Parent PersonID="P1" attr1="data">content<PersonName>Joe</PersonName>
</Parent>
<Parent PersonID="P2" attr2="data">
  <PersonName>Joe</PersonName>
</Parent>
<Parent PersonID="P3" attr3="data">
  <name>PersonName</name>
  <PersonName>Joe</PersonName>
</Parent>

Si la columna de datos xmltext contiene atributos del elemento raíz, estos atributos no se muestran en el esquema de datos XML y el analizador de MSXML no valida el fragmento del documento XML resultante. Por ejemplo:

SELECT 1 as Tag,
       0 as Parent,
       N'<overflow a="1"/>' as 'overflow!1!!xmltext'
FOR XML EXPLICIT, xmldata

Éste es el resultado. Observe que el atributo de desbordamiento a no aparece en el esquema devuelto:

<Schema name="Schema2" 
        xmlns="urn:schemas-microsoft-com:xml-data" 
        xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="overflow" content="mixed" model="open">
  </ElementType>
</Schema>
<overflow xmlns="x-schema:#Schema2" a="1">
</overflow> 

Vea también

Referencia

Usar el modo RAW
Usar el modo AUTO
Generar XML mediante FOR XML

Conceptos

Usar el modo PATH

Otros recursos

SELECT (Transact-SQL)

Ayuda e información

Obtener ayuda sobre SQL Server 2005