Serwer SQL zmienia strukturę XML po wstawieniu

15

Wstawiam trochę danych XML do kolumny XML na serwerze SQL, ale po wstawieniu danych zostały one zmienione przez serwer SQL. Oto dane, które wstawiam

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

Kiedy go czytam, wygląda to tak

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

Zwróć uwagę na drugą linię. Jest to problem, ponieważ zmienia sposób wyjścia transformacji XSLT. Pierwszy przykład utworzy spację między imieniem i nazwiskiem, a drugi nie utworzy spacji, więc będzie podobny do JohnJohnsen, a pierwszy będzie jak John Johnsen.

Czy jest jakiś sposób na rozwiązanie tego?

Pan Zach
źródło
Jest to problem, ponieważ zmienia to sposób wyjścia transformacji XSLT. Pierwsza linia utworzy spację między imieniem i nazwiskiem, a druga nie utworzy spacji między, więc będzie jak JohnJohnsen, a pierwsza będzie jak John Johnsen
Pan Zach
hmhm, właściwa spacja to jest „”, ale nie tylko spacja jak w tym komentarzu (nie widać tego)
a_vlad 27.11.18
1
Być może możesz użyć znaku kontrolnego, który nie istnieje w danych (np. _Lub ~), a następnie zastąpić go spacją w czasie prezentacji.
Aaron Bertrand

Odpowiedzi:

25

Możesz użyć xml:space = "preserve"w węzłach, w których chcesz zachować miejsce. Używanie xml: space jest „tylko sygnałem intencji”, ale SQL Server jest dla nas miły.

Dla jednego węzła

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

select @X;

Wynik:

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

Cały dokument:

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

select @X;

Wynik:

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

Inną opcją dla całego dokumentu jest użycie konwersji ze stylem 1 .

Zachowaj nieznaczne białe miejsca. To ustawienie stylu ustawia domyślną obsługę xml: space tak, aby pasowała do zachowania xml: space = "preserve".

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

select @X;
Mikael Eriksson
źródło
Ciekawe, że jest to wymagane. SQL Server nie powinien decydować, które białe znaki są „nieistotne” i dyskretnie usuwać je bez modyfikacji dokumentu!
Wyścigi lekkości z Monicą
3
@LightnessRacesinOrbit Jestem całkiem zadowolony z wdrożenia przez SQL Server. Formatowanie (białe znaki) w XML nie jest uważane za ważne, dopóki nie powiesz, że jest. Spójrz na ten przykład, aby zobaczyć liczbę węzłów, które faktycznie znajdują się w dokumencie, i jaki ma to wpływ na wielkość pamięci.
Mikael Eriksson
3
Uważam to za naruszenie specyfikacji, ponieważ tutaj dane są akceptowane jako XML i przechowywane jako XML, bez manipulacji lub transformacji lub jakiejkolwiek innej formy shenaniganów warstwy XML innych niż zwykłe przechowywanie dokumentu (pozornie), więc zachowanie powinno należą do „procesora”, a nie do „aplikacji”, dlatego nie mogą usuwać białych znaków .
Lekkość ściga się z Moniką
9

Ta strona dokumentacji SQL Server mówi

Dane są przechowywane w wewnętrznej reprezentacji, która ... może nie być identyczną kopią tekstu XML, ponieważ nie są zachowane następujące informacje: nieznaczne białe spacje, kolejność atrybutów, prefiksy przestrzeni nazw i deklaracja XML.

W twoim przykładzie przypuszczam, że uważa on, że biała przestrzeń środkowego znacznika nie jest znacząca i dlatego może dowolnie zmieniać reprezentację. Nie sądzę, żeby można to naprawić; tak właśnie SQL Server implementuje typ danych XML.

Obejścia obejmują obejście zastępcze zamiast białych znaków, jak mówi @Aaron. Konsument musi pamiętać, aby wstawić i usunąć te tokeny. Alternatywnie zdefiniuj kolumnę jako nvarchar zamiast XML. To z pewnością pozwoli zachować całą białą przestrzeń i wszelkie inne formatowanie. Szybki przykład:

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

Kolumna nvarchar zachowuje format wejściowy, kolumna XML nie.

Utracisz możliwość korzystania z XPATH w zapytaniach SQL. Jeśli XML jest niszczony tylko w aplikacji, jest to nieistotne. Ponadto ciąg znaków można skompresować, oszczędzając miejsce w DB, jeśli jest to dla Ciebie znaczące.

Michael Green
źródło
Prawdopodobnie nadal możesz używać XPATH w zapytaniach przeciwko wersji XML, nawet jeśli po prostu pozwolisz jej sformatować, pod warunkiem, że nie polegasz na trafieniu (lub spudłowaniu) za mało miejsca.
Aaron Bertrand
0

CDATAPodczas przechowywania danych możesz owinąć swoje wnętrze:

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

Wygląda na to, że serwer SQL zachowuje wewnętrznie miejsce, ale sam usuwa niepotrzebne CDATAznaczniki, gdy odzyskuje wynik SELECT. Na szczęście przestrzeń jest zachowywana przy ponownym użyciu wyniku takiego SELECT:

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

SET @Y = (SELECT @X)

SELECT @Y

Wynik będzie:

<text> </text>
Bruno
źródło
Próbowałem także CDATA, ale został również usunięty.
Zach Zach
@MrZach CDATA sam jest usuwany, ale przestrzeń pozostaje. (Wypróbowano w SQL Express 2016.)
Bruno
Dziwne, tutaj przestrzeń została usunięta. Pomyśl także wyrazić 2016 lub 2017 r.
Zach Zach