Il server SQL cambia la struttura XML quando viene inserito


15

Sto inserendo alcuni dati XML in una colonna XML nel server SQL ma dopo che i dati sono stati inseriti, sono stati modificati dal server SQL. Ecco i dati che inserisco

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

Quando lo rileggo, sembra così

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

Presta attenzione alla seconda riga. Questo è un problema perché cambia come sarà l'output della trasformazione XSLT. Il primo esempio creerà uno spazio tra nome e cognome, mentre il secondo non creerà alcuno spazio, quindi sarà come JohnJohnsen, mentre il primo sarà come John Johnsen.

C'è un modo per risolverlo?


È un problema, perché questo cambia il modo in cui sarà l'output della trasformazione XSLT. La prima riga creerà uno spazio tra il nome dato e il cognome, mentre la seconda non creerà alcun spazio tra di loro, quindi sarà come John Johnsen, mentre la prima sarà come John Johnsen
Mr Zach,

hmhm, lo spazio giusto è "" ma non solo uno spazio come in questo commento (non puoi vederlo)
a_vlad

1
Forse potresti usare un carattere di controllo che non esiste nei dati (come _o ~) e quindi sostituirlo con uno spazio al momento della presentazione.
Aaron Bertrand

Risposte:


25

Puoi usare xml:space = "preserve" sui nodi in cui si desidera mantenere lo spazio. Usando xml: lo spazio è "solo un segnale di intenti" ma SQL Server è gentile con noi qui.

Per un nodo

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

select @X;

Risultato:

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

Documento intero:

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

select @X;

Risultato:

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

Un'altra opzione per l'intero documento è usare convert con lo stile 1 .

Preserva spazi bianchi insignificanti. Questa impostazione di stile imposta la gestione xml: space predefinita in modo che corrisponda al comportamento di xml: space = "preservare".

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

select @X;

Interessante che questo sia richiesto. Non dovrebbe essere compito di SQL Server decidere quale spazio bianco sia "insignificante" e rimuoverlo silenziosamente senza modifiche al documento!
Corse di leggerezza con Monica il

3
@LightnessRacesinOrbit Sono abbastanza soddisfatto dell'implementazione da parte di SQL Server. La formattazione (spazio bianco) in XML non è considerata importante fino a quando non lo dici tu. Dai un'occhiata a questo esempio per vedere il numero di nodi che sono effettivamente nel documento e cosa fa per la dimensione della memoria.
Mikael Eriksson,

3
Lo considero una violazione delle specifiche, perché qui i dati sono accettati come XML e archiviati come XML, senza manipolazione o trasformazione o qualsiasi altra forma di shenanigans di livello XML oltre alla semplice memorizzazione del documento (apparentemente), quindi il comportamento dovrebbe cadere in quello di un "processore" piuttosto che di un "applicazione", e quindi non deve eliminare gli spazi bianchi .
Corse di leggerezza con Monica il

9

Questa pagina della documentazione di SQL Server dice

I dati sono memorizzati in una rappresentazione interna che ... potrebbe non essere una copia identica dell'XML di testo, poiché le seguenti informazioni non vengono conservate: spazi bianchi insignificanti, ordine degli attributi, prefissi dello spazio dei nomi e dichiarazione XML.

Per il tuo esempio suppongo che consideri lo spazio bianco del tag centrale come non significativo ed è quindi libero di refactoring la rappresentazione. Non penso che ci sia una soluzione per questo; è proprio come SQL Server implementa il tipo di dati XML.

Le soluzioni alternative includono l'uso di un segnaposto anziché di uno spazio bianco, come dice @Aaron. Il consumatore deve ricordare di inserire e rimuovere questi token. In alternativa, definire la colonna come nvarchar anziché XML. Ciò preserverà sicuramente tutto lo spazio bianco e qualsiasi altra formattazione. Un rapido esempio:

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 colonna nvarchar conserva il formato di input, la colonna XML no.

Perderai la possibilità di utilizzare XPATH nelle query SQL. Se l'XML viene distrutto solo nell'applicazione, questo è irrilevante. Inoltre, la stringa di caratteri potrebbe essere compressa risparmiando spazio nel DB, se questo è significativo per te.


Probabilmente potresti ancora usare XPATH nelle query contro la versione XML, anche se lo lasci semplicemente riformattare, purché non ti affidi a un hit (o miss) per lo spazio insignificante lì.
Aaron Bertrand

0

È possibile avvolgere lo spazio all'interno CDATAdurante la memorizzazione dei dati:

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

Sembra che il server SQL mantenga quindi lo spazio internamente, ma rimuove il CDATAmarkup non necessario stesso quando si ottiene il risultato utilizzando nuovamente SELECT. Fortunatamente, lo spazio viene mantenuto quando si riutilizza il risultato di un tale SELECT:

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

SET @Y = (SELECT @X)

SELECT @Y

Il risultato sarà:

<text> </text>

Ho anche provato CDATA ma è stato anche rimosso.
Mr Zach,

@MrZach CDATA stesso viene rimosso, ma lo spazio rimane. (Provato su SQL Express 2016.)
Bruno,

Strano, qui lo spazio è stato rimosso. Pensa anche a esprimere il 2016 o il 2017
Mr Zach,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.