Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

dev2dev

XML-owe przypadki – transformacja (nie)odwracalna

Zapytanie z opcją FOR XML generujące obiekt XML i metoda nodes() zastosowana do niego mogą stanowić wzajemnie odwrotne transformacje. Zobaczmy to na przykładzie. Najpierw przygotujemy tablicę z danymi testowymi:

declare @t table(x int)

insert into @t values
(3),(2),(1),(0),(3)


i teraz wykonamy proste zapytania:


declare @xml xml

-- 1. Zapytanie podstawowe ----------------------------------
select * from @t

-- 2. xsinil ------------------------------------------------
set @xml = (select * from @t for xml raw, elements xsinil)

select t.c.value('./x[1]','int') x
from @xml.nodes('/row') t(c)

-- 3. absent ------------------------------------------------
set @xml = (select * from @t for xml raw, elements absent)

select t.c.value('./x[1]','int') x
from @xml.nodes('/row') t(c)


Pierwsze zapytanie zwraca po prostu zawartość tabeli @t.

W drugim zapytaniu tworzymy obiekt XML poprzez najprostszą formę klauzuli FOR XML czyli RAW, ale tworzymy go z opcją ELEMENTS XSINIL. Opcja ta w przypadku gdy wyrażenie tworzące element ma wartość NULL tworzy element z atrybutem xsi:nil="true" i z pustą zawartością. Następnie na obiekt XML działamy metodą nodes() aby przywrócić danym formę tabelaryczną.

W trzecim zapytaniu tworzymy obiekt XML poprzez najprostszą formę klauzuli FOR XML czyli RAW, ale tworzymy go z opcją ELEMENTS ABSENT. Opcja ta w przypadku gdy wyrażenie tworzące element ma wartość NULL pomija tworzenie elementu XML z nim związanego. Następnie na obiekt XML działamy metodą nodes() aby przywrócić danym formę tabelaryczną.

Jak łatwo się przekonać, wszystkie zapytania zwrócą ten sam rezultat (czyli z obiektu XML wygenerowanego zapytaniem z klauzulą FOR XML otrzymaliśmy ponownie tablicę dzięki zastosowaniu metody nodes() na tym obiekcie - czyli stanowią wzajemnie odwrotne operacje):



Skoro nam tak dobrze idzie to wprowadzimy zaburzenie do naszych danych testowych w sposób następujący:


declare @t table(x int)

insert into @t values
(3),(2),(1),(0),(null)

Wykonanie tych samych zapytań da jednak zaskakujący efekt:



Widać z tego, że drugie zapytanie zwróciło wartość zero zamiast oczekiwanej wartości NULL. Czy można temu zachowaniu zapobiec? Tak, wystarczy inaczej zapisać drugie zapytanie, rzutując element zwracany przez metodę value() na typ xs:int?

set @xml = (select * from @t for xml raw, elements xsinil)

select t.c.value('./x[1] cast as xs:int?','int') x
from @xml.nodes('/row') t(c)



I wszystko wydawałoby się już poukładane, do momentu gdy nie zastosujemy kolumny typu varchar:

declare @t table(x int, y varchar(100))

insert into @t values
(3,'a'),(2,'b'),(1,'c'),(0,'d'),(null,null)

declare @xml xml

set @xml = (select * from @t for xml raw, elements xsinil)

select
    t.c.value('./x[1] cast as xs:int?','int') x,
    t.c.value('./y[1] cast as xs:string?','varchar(100)') y
from @xml.nodes('/row') t(c)


W tym wypadku zapytanie zwróci nam wynik:



i widzimy, że w piątym wierszu jedynie w kolumnie y zamiast oczekiwanej wartości NULL mamy pusty string. Nawet zastosowanie rzutowania na xs:string? nie daje spodziewanego rezultatu jak przy rzutowaniu na typ xs:int? przy kolumnie typu int. W rzutowaniu cast as xs:int? oraz cast as xs:string? istotne są znaki zapytania na końcu definicji typu docelowego. Mówią one, że wartość atomowa uzyskiwana przez zapytanie XQuery w metodzie value() a będąca przedmiotem rzutowania może przyjmować wartość null. Dlaczego jednak działa to dla typu int a nie działa dla typu varchar pozostaje dla mnie tajemnicą. Wydaje mi się, że za tym wszystkich gdzieś przebija .NET-owe podejście do XML-a.
Opublikowane 27 maja 2010 20:45 przez marekpow
Filed under: , ,

Komentarze:

 

dotnetomaniak.pl said:

Dziękujemy za publikację - Trackback z dotnetomaniak.pl

maja 28, 2010 07:28
Komentarze anonimowe wyłączone
W oparciu o Community Server (Personal Edition), Telligent Systems