El servidor SQL cambia la estructura XML cuando se inserta

15

Estoy insertando algunos datos XML en una columna XML en el servidor SQL, pero después de que los datos se hayan insertado, el servidor sql los ha cambiado. Aquí están los datos que inserto

              <xsl:value-of select="name/n/given" />
            <xsl:text> </xsl:text>
          <xsl:value-of select="name/n/family" />

Cuando lo leo de nuevo, se ve así

              <xsl:value-of select="name/n/given" />
          <xsl:text />
          <xsl:value-of select="name/n/family" />

Presta atención a la segunda línea. Esto es un problema porque cambia la forma en que será la salida de la transformación XSLT. El primer ejemplo creará un espacio entre el apellido y el apellido, mientras que el segundo no creará ningún espacio, por lo que será como JohnJohnsen, mientras que el primero será como John Johnsen.

¿Hay alguna forma de resolver esto?

Sr. Zach
fuente
Es un problema, porque esto cambia cómo será la salida de la transformación XSLT. La primera línea creará un espacio entre el nombre de pila y el apellido, mientras que la segunda no creará ningún espacio entre ellos, por lo que será como JohnJohnsen, mientras que la primera será como John Johnsen
Sr. Zach el
hmhm, espacio adecuado es "" pero no solo un espacio como en este comentario (no puedes verlo)
a_vlad
1
Tal vez podría usar un carácter de control que no existe en los datos (como _o ~) y luego reemplazarlo con un espacio en el momento de la presentación.
Aaron Bertrand

Respuestas:

25

Puede usar xml:space = "preserve"en los nodos donde desea mantener el espacio. Usar xml: space es "solo una señal de intención", pero el servidor SQL es amable con nosotros aquí.

Para un nodo

declare @X xml =
'<root>
  <element xml:space = "preserve"> </element>
  <element> </element>
</root>'

select @X;

Resultado:

<root>
  <element xml:space="preserve"> </element>
  <element />
</root>

Documento completo:

declare @X xml =
'<root xml:space = "preserve">
  <element> </element>
  <element> </element>
</root>'

select @X;

Resultado:

<root xml:space="preserve">
  <element> </element>
  <element> </element>
</root>

Otra opción para todo el documento es usar convertir con estilo 1 .

Preservar espacios en blanco insignificantes. Esta configuración de estilo establece el manejo predeterminado de xml: space para que coincida con el comportamiento de xml: space = "preserve".

declare @X xml = convert(xml, 
'<root>
  <element> </element>
  <element> </element>
</root>', 1)

select @X;
Mikael Eriksson
fuente
Interesante que esto sea requerido. ¡No debería ser el mandato de SQL Server decidir qué espacio en blanco es "insignificante" y eliminarlo en silencio sin modificaciones del documento!
Lightness compite con Monica el
3
@LightnessRacesinOrbit Estoy bastante contento con la implementación de SQL Server. El formato (espacio en blanco) en XML no se considera importante hasta que usted lo dice. Eche un vistazo a este ejemplo para ver la cantidad de nodos que están realmente en el documento y qué hace con el tamaño de almacenamiento ...
Mikael Eriksson
3
Considero que es una violación de especificaciones, porque aquí los datos se aceptan como XML y se almacenan como XML, sin manipulación ni transformación ni ninguna otra forma de travesuras de capa XML que no sea simplemente almacenar el documento (aparentemente), por lo que el comportamiento debería caer en la de un "procesador" en lugar de una "aplicación", y por lo tanto no debe eliminar espacios en blanco .
Lightness compite con Monica el
9

Esta página de la documentación de SQL Server dice

Los datos se almacenan en una representación interna que ... puede no ser una copia idéntica del texto XML, porque no se conserva la siguiente información: espacios en blanco insignificantes, orden de atributos, prefijos de espacio de nombres y declaración XML.

Para su ejemplo, supongo que considera que el espacio en blanco de la etiqueta central no es significativo y, por lo tanto, es libre de refactorizar la representación. No creo que haya una solución para esto; así es como SQL Server implementa el tipo de datos XML.

Las soluciones alternativas incluirían el uso de un marcador de posición en lugar de espacios en blanco como dice @Aaron. El consumidor debe recordar insertar y quitar estas fichas. Alternativamente, defina la columna como nvarchar en lugar de XML. Esto definitivamente preservará todo el espacio en blanco y cualquier otro formato. Un ejemplo rápido:

create table x(i nvarchar(99), j xml);
insert x values ('<a> </a>', '<a> </a>');  -- note the space
select * from x

i           j
----------  -------
<a> </a>    <a />  

La columna nvarchar conserva el formato de entrada, la columna XML no.

Perderá la capacidad de usar XPATH en consultas SQL. Si el XML solo se destruye en la aplicación, esto es irrelevante. Además, la cadena de caracteres podría comprimirse ahorrando espacio en la base de datos, si esto es importante para usted.

Michael Green
fuente
Probablemente aún podría usar XPATH en consultas contra la versión XML, incluso si simplemente lo deja formatear, siempre y cuando no confíe en un hit (o miss) para el espacio insignificante allí.
Aaron Bertrand
0

Podrías envolver tu espacio CDATAal almacenar los datos:

<xsl:text><![CDATA[ ]]></xsl:text>

Parece que el servidor SQL mantiene el espacio internamente, pero elimina el CDATAmarcado innecesario en sí mismo al recuperar el resultado usando SELECT. Afortunadamente, el espacio se mantiene cuando se reutiliza el resultado de tal SELECT:

DECLARE @X XML = '<text><![CDATA[ ]]></text>'
DECLARE @Y XML

SET @Y = (SELECT @X)

SELECT @Y

El resultado será:

<text> </text>
Bruno
fuente
También probé CDATA pero también fue eliminado.
Sr. Zach
@MrZach CDATA se elimina, pero el espacio permanece. (Probado en SQL Express 2016.)
Bruno
Extraño, aquí se eliminó el espacio. Piense también express 2016 o 2017
Mr Zach