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

15

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

Mr Zach
la source
C'est un problème, car cela change la façon dont la sortie de la transformation XSLT sera. La première ligne créera un espace entre le prénom et le nom de famille, tandis que la seconde ne créera aucun espace entre, donc ce sera comme JohnJohnsen, tandis que la première sera comme John Johnsen
M. Zach
hmhm, l'espace approprié c'est "" mais pas seulement un espace comme dans ce commentaire (vous ne pouvez pas le voir)
a_vlad
1
Vous pouvez peut-être utiliser un caractère de contrôle qui n'existe pas dans les données (comme _ou ~), puis le remplacer par un espace au moment de la présentation.
Aaron Bertrand

Réponses:

25

Vous pouvez utiliser xml:space = "preserve"sur les nœuds où vous souhaitez conserver l'espace. Utiliser xml: space n'est "qu'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 consiste à utiliser la conversion 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;
Mikael Eriksson
la source
Intéressant que cela soit nécessaire. Il ne devrait pas appartenir à SQL Server de décider quel espace est "insignifiant" et de le supprimer silencieusement sans modification de document!
Courses de légèreté avec Monica
3
@LightnessRacesinOrbit Je suis assez satisfait de l'implémentation par SQL Server. Le formatage (espaces blancs) en XML n'est pas considéré comme important tant que vous ne le dites pas. Jetez un oeil à cet exemple pour voir le nombre de nœuds qui sont réellement dans le document et ce qu'il fait sur la taille du stockage.
Mikael Eriksson
3
Je considère qu'il s'agit d'une violation des spécifications, car ici les données sont acceptées en XML et stockées en XML, sans manipulation ni transformation ou toute autre forme de manigances de couche XML autre que simplement le stockage du document (ostensiblement), donc le comportement devrait tomber dans celui d'un "processeur" plutôt que d'une "application", et ne doit donc pas dépouiller les espaces .
Courses de légèreté avec Monica
9

Cette page de la documentation de SQL Server indique

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 consisteraient à utiliser 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.

Michael Green
la source
Vous pourriez probablement encore utiliser XPATH dans les requêtes sur la version XML, même si vous le laissez simplement reformater, tant que vous ne comptez pas sur un hit (ou un miss) pour l'espace insignifiant qui s'y trouve.
Aaron Bertrand
0

Vous pouvez envelopper votre espace CDATAlors du stockage des données:

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

Il semble que SQL Server conserve ensuite l'espace en interne, mais supprime le CDATAbalisage inutile lui-même lors de la récupération du résultat à l'aide 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>
Bruno
la source
A également essayé CDATA mais il a également été supprimé.
M. Zach
@MrZach CDATA lui-même est supprimé, mais l'espace reste. (Testé sur SQL Express 2016.)
Bruno
Étrange, ici l'espace a été supprimé. Pensez aussi à exprimer 2016 ou 2017
Mr Zach