Klasa XML_Serializer służy do konwersji tablic PHP na kod XML. Przekształcenie odwrotne realizuje klasa XML_Unserialize. Razem stanowią bardzo wygodne narzędzie, dzięki któremu zarówno generowanie, jak i odczyt kodu XML sprowadza się do operacji na tablicach.
Klasa XML_Serializer
Klasa {stala}XML_Serializer{/stala} służy do konwersji tablic PHP w kod XML. Ma ona metody pozwalające na precyzyjne określenie sposobu konwersji, w szczególności struktury otrzymywanego kodu XML. Znaczniki XML powstają na podstawie indeksów tablic asocjacyjnych lub – w przypadku tablic indeksowanych – jawnie wskazanych transformacji. Dodatkowe opcje konwersji umożliwiają m.in. wskazanie danych, które zostaną przekształcone w atrybuty.
Konwersja tablicy w kod XML
Najprostsze użycie klasy {stala}XML_Serializer{/stala} sprowadza się do wywołania dwóch metod: {stala}serialize(){/stala} oraz {stala}getSerializedData(){/stala}.
Najpierw tworzymy tablicę, która zostanie przekształcona w kod XML. W przykładzie tablica ta nazywa się {stala}$dane{/stala}. Następnie dołączamy bibliotekę {stala}XML_Serializer{/stala} i tworzymy obiekt {stala}$serializer{/stala}. Metoda {stala}serialize(){/stala} przekazuje do utworzonego obiektu tablicę {stala}$dane{/stala}, zaś metoda {stala}getSerializedData(){/stala} zwraca wygenerowany kod XML. Pełny skrypt przyjmie postać:
serialize($dane);
echo $serializer->getSerializedData();
?>
Wygenerowany XML będzie zawierał jeden element {stala}array{/stala} oraz trzy elementy {stala}XML_Serializer_Tag{/stala}:
12
2
2008
Tablica asocjacyjna
Jeśli tablica {stala}$dane{/stala} będzie tablicą asocjacyjną:
$dane = array(
\'dzien\' => \'12\',
\'miesiac\' => \'2\',
\'rok\' => \'2008\'
);
wówczas ten sam skrypt PHP:
$serializer = new XML_Serializer();
$serializer->serialize($dane);
echo $serializer->getSerializedData();
wygeneruje nieco inny kod XML. Tym razem elementy XML zostały przygotowane na podstawie indeksów z tablicy {stala}$dane{/stala}:
12
2
2008
Opcje
W celu otrzymania ładnie sformatowanego kodu XML poprzedzonego prologiem należy ustalić dodatkowe opcje konwersji. Metoda {stala}setOption(){/stala} ustala różne opcje pracy klasy {stala}XML_Serializer{/stala}. W celu dodania prologu XML należy wykorzystać opcję {stala}XML_SERIALIZER_OPTION_XML_DECL_ENABLED{/stala}. Opcje z przyrostkami {stala}_XML_ENCODING{/stala}, {stala}_INDENT{/stala} oraz {stala}_ROOT_NAME{/stala} odpowiadają za kodowanie, wcięcia oraz nazwę elementu głównego:
$serializer->setOption(XML_SERIALIZER_OPTION_XML_DECL_ENABLED, true);
$serializer->setOption(XML_SERIALIZER_OPTION_XML_ENCODING, \'utf-8\');
$serializer->setOption(XML_SERIALIZER_OPTION_INDENT, \' \');
$serializer->setOption(XML_SERIALIZER_OPTION_ROOT_NAME, \'data\');
$serializer->serialize($dane);
Tym razem otrzymamy kompletny i ładnie wcięty kod XML:
12
2
2008
Tablica opcji
Wszystkie opcje, sterujące pracą klasy {stala}XML_Serializer{/stala} mogą być zawarte w jednej tablicy asocjacyjnej. Poniższa tablica {stala}$options{/stala} ustala identyczne opcje jak w poprzednim przykładzie:
$options = array(
\'addDecl\' => true,
\'indent\' => \' \',
\'rootName\' => \'data\',
\'encoding\' => \'utf-8\',
);
$serializer->setOptions($options);
Niestety nazewnictwo opcji nie jest ujednolicone. Nazw opcji przekazywanych w tablicy nie da się ustalić na podstawie pełnych nazw. Tabela 1 zawiera nazewnictwo opcji stosowane zarówno w przypadku, gdy każda opcja jest przekazywana osobnym wywołaniem, jak i w przypadku użycia tablicy.
Jednowymiarowa tablica indeksowana
Konwertowanie tablic indeksowanych wymaga ustalenia nazw elementów XML. Przekształcając tablicę jednowymiarową:
$owoce = array(
\'jabłko\',
\'gruszka\',
\'banan\',
...
);
wystarczy podać nazwę elementu domyślnego {stala}defaultTagName{/stala}:
$serializer = new XML_Serializer();
$options = array(
\'rootName\' => \'owoce\',
\'defaultTagName\' => \'owoc\'
);
$serializer->setOptions($options);
$serializer->serialize($owoce);
echo $serializer->getSerializedData();
Otrzymamy wówczas kod XML:
jabłko
gruszka
...
Wielowymiarowe tablice indeksowane
Przekształcając wielowymiarowe tablice indeksowane możemy precyzyjnie wskazać nazwy elementów generowanych na podstawie każdej tablicy. Tablica {stala}$dane{/stala} zawiera dwie składowe o indeksach owoce oraz warzywa. Składowe te są tablicami indeksowanymi:
$dane = array(
\'owoce\' => array(
\'gruszka\',
\'jabłko\',
...
),
\'warzywa\' => array(
\'marchewka\',
\'pietruszka\',
...
)
);
W takiej sytuacji opcja {stala}defaultTagName{/stala} jest tablicą. Jej indeksami są indeksy w tablicy {stala}$dane{/stala}, zaś wartościami – nazwy znaczników XML:
$options = array(
\'rootName\' => \'warzywa-i-owoce\',
\'defaultTagName\' => array(\'owoce\' => \'owoc\', \'warzywa\' => \'warzywo\')
);
$serializer->setOptions($options);
$serializer->serialize($dane);
echo $serializer->getSerializedData();
W ten sposób elementy tablicy {stala}$dane[\’owoce\’]{/stala} będą zawarte w znacznikach {html}
gruszka
jabłko
...
marchewka
pietruszka
...
Atrybuty
Przekształcaniem elementów tablicy danych w atrybuty steruje opcja{stala} scalarAsAttributes{/stala}. Jeśli przyjmuje ona wartość {stala}true{/stala}, to wszystkie dane będą zawarte w kodzie XML w postaci atrybutów.
Tablica:
$dane = array(
\'imie\' => \'Jan\',
\'nazwisko\' => \'Nowak\',
\'dataurodzenia\' => array(
\'dzien\' => \'24\',
\'miesiac\' => \'5\',
\'rok\' => \'1967\'
)
);
zostanie przekształcona przez skrypt:
$options = array(
\'rootName\' => \'data\',
\'scalarAsAttributes\' => true
);
$serializer->setOptions($options);
$serializer->serialize($dane);
echo $serializer->getSerializedData();
do postaci:
Znaczniki {html}
Jan
a w atrybuty:
imie=\"Jan\"
Wybiórcze stosowanie atrybutów
Jeśli przekształceniu w atrybuty ma podlegać tylko część danych z tablicy, wówczas jako wartość opcji {stala}scalarAsAttributes{/stala} należy przekazać tablicę asocjacyjną.
Dane:
$dane = array(
array(
\'imie\' => \'Jan\',
\'nazwisko\' => \'Nowak\',
\'plec\' => \'M\',
\'dataurodzenia\' => array(
\'dzien\' => \'24\',
\'miesiac\' => \'5\',
\'rok\' => \'1967\'
)
),
...
);
przekształcone skryptem:
$options = array(
\'rootName\' => \'osoby\',
\'defaultTagName\' => \'osoba\',
\'scalarAsAttributes\' => array(
\'osoba\' => array(\'plec\'),
\'dataurodzenia\' => array(\'rok\', \'dzien\')
)
);
$serializer->setOptions($options);
$serializer->serialize($dane);
echo $serializer->getSerializedData();
wyprodukują kod:
Jan
Nowak
5
...
Tablica, będąca wartością opcji {stala}scalarAsAttributes{/stala}:
array(
\'osoba\' => array(\'plec\'),
\'dataurodzenia\' => array(\'rok\', \'dzien\')
)
wskazuje, że znaczniki {html}
Przekształcenia
Przekształcając składowe tablicy w kod XML klasa {stala}XML_Serializer{/stala} może dokonać dodatkowych konwersji. Na przykład w celu usunięcia białych znaków, otaczających treść elementów XML, należy wywołać funkcję {stala}trim(){/stala}. W tym celu w tablicy opcji dodajemy element o indeksie {stala}encodeFunction{/stala} i wartości {stala}trim{/stala}:
$owoce = file(\'owoce.txt\');
$options = array(
\'encodeFunction\' => \'trim\'
);
$serializer->setOptions($options);
$serializer->serialize($owoce);
echo $serializer->getSerializedData();
Zestawienie najważniejszych opcji klasy {stala}XML_ Serializer{/stala} jest zawarte w tabeli 1.
XML_Serializer – przykłady
Klasę {stala}XML_Serializer{/stala} możemy wykorzystać między innymi do konwersji plików tekstowych w dokumenty XML. W takim przypadku całe zadanie sprowadza się do odczytania i przekształcenia pliku tekstowego w tablicę. Resztą zajmuje się klasa {stala}XML_Serializer{/stala}.
Pierwszy z przykładów [listing 1] przedstawia najprostszy sposób konwersji. Produkowany przez niego XML nie zawiera atrybutów.
true,
\'indent\' => \' \',
\'rootName\' => \'nagrody\',
\'encoding\' => \'utf-8\',
\'defaultTagName\' => \'nagroda\'
);
$serializer->setOptions($options);
$dane = array();
$plik = file(\'nobel.txt\');
foreach ($plik as $linia) {
$e = explode(\'|\', trim($linia));
$tmp = array(
\'rok\' => $e[0],
\'fizyka\' => $e[1],
\'chemia\' => $e[2],
\'medycyna\' => $e[3],
\'literatura\' => $e[4],
\'pokojowa\' => $e[5]
);
array_push($dane, $tmp);
}
$serializer->serialize($dane);
$wynik = $serializer->getSerializedData();
echo $wynik;
?>
Drugi przykład [listing 2] pokazuje, w jaki sposób plik tekstowy przekształcić do formatu XML, w którym jeden z elementów zawiera trzy atrybuty.
$serializer = new XML_Serializer();
$options = array(
\'addDecl\' => true,
\'indent\' => \' \',
\'rootName\' => \'mecze\',
\'encoding\' => \'utf-8\',
\'defaultTagName\' => \'mecz\',
\'scalarAsAttributes\' => array(
\'mecz\' => array(\'rok\', \'miesiac\', \'dzien\')
)
);
$serializer->setOptions($options);
$dane = array();
$plik = file(\'mecze.txt\');
foreach ($plik as $linia) {
$e = explode(\'|\', trim($linia));
$tmp = array(
\'kolejka\' => $e[0],
\'gospodarz\' => $e[1],
\'przeciwnik\' => $e[2],
\'wynik\' => array(
\'koncowy\' => array(
\'gol1\' => $e[3],
\'gol2\' => $e[4],
),
\'do-przerwy\' => array(
\'gol3\' => $e[5],
\'gol4\' => $e[6],
)
),
\'rok\' => $e[7],
\'miesiac\' => $e[8],
\'dzien\' => $e[9],
\'godz\' => $e[10],
\'min\' => $e[11],
\'sedzia-glowny\' => array(
\'imie\' => $e[12],
\'nazwisko\' => $e[13],
\'miasto\' => $e[14],
),
\'sedzia-liniowy-1\' => array(
\'imie\' => $e[15],
\'nazwisko\' => $e[16],
),
\'sedzia-liniowy-2\' => array(
\'imie\' => $e[15],
\'nazwisko\' => $e[16],
)
);
array_push($dane, $tmp);
}
$serializer->serialize($dane);
$wynik = $serializer->getSerializedData();
echo $wynik;
Wreszcie przykład trzeci [listing 3] demonstruje konwersję, w której kilka elementów XML otrzyma atrybuty.
$serializer = new XML_Serializer();
$options = array(
\'addDecl\' => true,
\'indent\' => \' \',
\'rootName\' => \'turniej-czterech-skoczni\',
\'encoding\' => \'utf-8\',
\'defaultTagName\' => array(
\'turniej-czterech-skoczni\' => \'zawody\',
\'miejsca\' => \'miejsce\'
),
\'scalarAsAttributes\' => array(
\'zawody\' => true,
\'miejsce\' => array(\'numer\')
)
);
$serializer->setOptions($options);
$dane = array();
$plik = file(\'tcs.txt\');
foreach ($plik as $linia) {
$e = explode(\'|\', trim($linia));
$tmp = array(
\'rok\' => $e[0],
\'miejsca\' => array(
array(
\'numer\' => $e[1],
\'imie\' => $e[2],
\'nazwisko\' => $e[3],
\'kraj\' => $e[4],
),
array(
\'numer\' => $e[5],
\'imie\' => $e[6],
\'nazwisko\' => $e[7],
\'kraj\' => $e[8],
),
array(
\'numer\' => $e[9],
\'imie\' => $e[10],
\'nazwisko\' => $e[11],
\'kraj\' => $e[12],
),
)
);
array_push($dane, $tmp);
}
$serializer->serialize($dane);
$wynik = $serializer->getSerializedData();
echo $wynik;
Klasa XML_Unserializer
Klasa {stala}XML_Unserializer{/stala} wykonuje przekształcenie odwrotne w stosunku do {stala}XML_Serializer{/stala}. Na podstawie kodu XML produkuje tablicę PHP.
Podstawowe użycie
W celu przetestowania klasy {stala}XML_Unserialize{/stala} najpierw przygotowujemy kod XML. W pliku {stala}data.xml{/stala} zapisujemy następujące dane:
12
2
2008
W skrypcie dołączamy plik XML/Unserializer.php, następnie tworzymy obiekt {stala}$unserializer{/stala}, od czytujemy plik data.xml, po czym wykonujemy przekształcenie:
require_once \'XML/Unserializer.php\';
$unserializer = new XML_Unserializer();
$xml = file_get_contents(\'data.xml\');
$unserializer->unserialize($xml);
$wynik = $unserializer->getUnserializedData();
Otrzymany wynik możemy przeanalizować wywołując funkcję {stala}var_dump(){/stala}:
echo \'\';
var _ dump($wynik);
echo \'
\';
Wyświetlony wynik dowodzi, że na podstawie pliku data.xml otrzymaliśmy tablicę asocjacyjną:
array(3) {
[\"dzien\"]=>
string(2) \"12\"
[\"miesiąc\"]=>
string(1) \"2\"
[\"rok\"]=>
string(4) \"2008\"
}
Po wykonanej transformacji element główny kodu XML możemy odczytać, wywołując metodę {stala}getRootName(){/stala}:
echo $unserializer->getRootName();
Odczyt pliku
Domyślnie parametrem metody {stala}unserialize(){/stala} jest kod XML. Stosując drugi, opcjonalny parametr, możemy przekazać do metody {stala}unserialize(){/stala} nazwę pliku. Eliminuje to konieczność stosowania funkcji {stala}fiale_get_contents(){/stala}:
$unserializer->unserialize(\'mecze.xml\', true);
Parsing atrybutów
W przypadku braku jakichkolwiek opcji klasa {stala}XML_Unserialize{/stala} nie przetwarza atrybutów. Jeśli w otrzymywanej tablicy chcemy uzyskać atrybuty, należy użyć opcji {stala}parseAttributes{/stala}:
$unserializer = new XML_Unserializer();
$options = array(
\'parseAttributes\' => true,
);
$unserializer->setOptions($options);
$unserializer->unserialize(\'tcs-atrybuty.xml\', true);
$wynik = $unserializer->getUnserializedData();
lub – alternatywnie – opcji {stala}XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE{/stala}:
$unserializer->setOption(XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE, true);
Wówczas zwracane tablice będą zawierały także wszystkie atrybuty, jakie wystąpiły w kodzie XML. Jeśli dodatkowo użyta zostanie opcja {stala}attributesArray{/stala} (lub {stala}XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY{/stala}):
$options = array(
\'parseAttributes\' => true,
\'attributesArray\' => \' _ a\'
);
to wszystkie atrybuty każdego elementu trafią do tablicy {stala}_a{/stala}.
Konwersja formatu XML
Jako przykład wykorzystania obu klas {stala}XML_Serializer{/stala} oraz {stala}XML_Unserializer{/stala} posłuży skrypt konwertujący dokumenty XML. Pojedynczy plik może zawierać jeden rekord bazy danych:
2007
1
...
lub listę rekordów:
2007
1
...
2005
8
...
...
Konwersja wielu plików zawierających po jednym rekordzie w jeden plik z listą rekordów jest przedstawiona na listingu 4.
unserialize($plk, true);
$wynik = $unserializer->getUnserializedData();
array_push($wszystkie, $wynik);
}
$serializer = new XML_Serializer();
$options = array(
\'addDecl\' => true,
\'indent\' => \' \',
\'rootName\' => \'artykuly\',
\'encoding\' => \'utf-8\',
\'defaultTagName\' => \'artykul\',
);
$serializer->setOptions($options);
$serializer->serialize($wszystkie);
$wynik = $serializer->getSerializedData();
file_put_contents(\'artykuly.xml\', $wynik);
?>
Listing 5 przedstawia konwersję odwrotną: jeden plik, zawierający listę rekordów, jest zamieniany w wiele plików, z których każdy zawiera jeden rekord.
unserialize(\'artykuly.xml\', true);
$artykuly = $unserializer->getUnserializedData();
$ile = count($artykuly[\'artykul\']);
for ($i = 0; $i < $ile; $i++) {
$serializer = new XML_Serializer();
$options = array(
\'addDecl\' => true,
\'indent\' => \' \',
\'rootName\' => \'artykul\',
\'encoding\' => \'utf-8\'
);
$serializer->setOptions($options);
$serializer->serialize($artykuly[\'artykul\'][$i]);
$wynik = $serializer->getSerializedData();
file_put_contents(\'artykuly/\' . $i . \'.xml\', $wynik);
}
?>