web-dev-qa-db-fra.com

Le serveur SQL change la structure XML lorsqu'il est inséré

J'insère des données XML dans une colonne XML dans SQL Server mais une fois les données insérées, elles ont été modifiées par SQL Server. Voici les données que j'insère

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

Quand je le relis, ça ressemble à ça

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

Faites attention à la deuxième ligne. Il s'agit d'un problème car il change la façon dont la sortie de la transformation XSLT sera. Le premier exemple créera un espace entre le prénom et le nom de famille, tandis que le second ne créera aucun espace, donc ce sera comme JohnJohnsen, tandis que le premier sera comme John Johnsen.

Existe-t-il un moyen de résoudre ce problème?

15
Mr Zach

Vous pouvez utiliser xml:space = "preserve" sur les nœuds où vous souhaitez conserver l'espace. Utiliser xml: space est "seulement un signal d'intention" mais le serveur SQL est bon pour nous ici.

Pour un nœud

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

select @X;

Résultat:

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

Document entier:

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

select @X;

Résultat:

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

Une autre option pour le document entier est d'utiliser convertir avec le style 1 .

Conservez un espace blanc insignifiant. Ce paramètre de style définit la gestion par défaut de xml: space pour qu'elle corresponde au comportement de xml: space = "preserve".

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

select @X;
25
Mikael Eriksson

Cette page de la documentation de SQL Server dit

Les données sont stockées dans une représentation interne qui ... peut ne pas être une copie identique du texte XML, car les informations suivantes ne sont pas conservées: espaces blancs insignifiants, ordre des attributs, préfixes d'espace de noms et déclaration XML.

Pour votre exemple, je suppose qu'il considère que l'espace blanc de la balise du milieu n'est pas significatif et est donc libre de refactoriser la représentation. Je ne pense pas qu'il y ait de solution à cela; c'est simplement la façon dont SQL Server implémente le type de données XML.

Les solutions de contournement incluraient l'utilisation d'un espace réservé au lieu d'un espace blanc comme le dit @Aaron. Le consommateur ne doit pas oublier d'insérer et de retirer ces jetons. Vous pouvez également définir la colonne comme nvarchar au lieu de XML. Cela préservera certainement tout l'espace blanc et tout autre formatage. Un exemple rapide:

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 colonne nvarchar conserve le format d'entrée, contrairement à la colonne XML.

Vous perdrez la possibilité d'utiliser XPATH dans les requêtes SQL. Si le XML n'est déchiqueté que dans l'application, cela n'a pas d'importance. De plus, la chaîne de caractères peut être compressée en économisant de l'espace dans la base de données, si cela est important pour vous.

9
Michael Green

Vous pouvez envelopper votre espace dans CDATA lors du stockage des données:

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

Il semble que SQL Server conserve ensuite l'espace en interne, mais supprime lui-même le balisage CDATA inutile lors de la récupération du résultat à l'aide de SELECT. Heureusement, l'espace est conservé lors de la réutilisation du résultat d'un tel SELECT:

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

SET @Y = (SELECT @X)

SELECT @Y

Le résultat sera:

<text> </text>
0
Bruno