Connect with us

Cześć, czego szukasz?

Internet Maker

XML_Serializer, XML_Unserializer – dwukierunkowe transformacje tablic PHP w XML

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}{/html}, a elementy tablicy {html}$dane\’warzywa\’]{/html} – w znacznikach {html}{/html}. Otrzymany kod XML będzie następujący:


   
      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}{/html} powstały na podstawie indeksu w tablicy. Wszystkie pozostałe dane z tablicy są przekształcane nie tak jak do tej pory w elementy, np.:

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}{/html} będą posiadały atrybuty {stala}plec{/stala}, a elementy {html}{/html} – atrybuty {stala}rok{/stala} oraz {stala}dzien{/stala}. Wszystkie pozostałe składowe tablicy {stala}$dane{/stala} będą przekształcane w znaczniki.

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);
}

?>

Może cię też zainteresować

Internet Maker

PHP zdobył przed laty popularność jako język skryptowy do tworzenia stron internetowych. Wzięła się ona z pewnością stąd, że jeszcze kilka lat temu nie było alternatywy dla szybkiego, prostego...

Internet Maker

To już trzecie wydanie książki Andrzeja Kierzkowskiego, tłumaczącej nie tylko podstawy języka PHP 5, ale także zawierającej wiele praktycznych ćwiczeń. Autor od lat pisze książki dotyczące programowania w tym języku,...

Internet Maker

Język PHP jest wykorzystywany najczęściej do tworzenia skryptów pracujących na tekście. Jednak dzięki dołączonej do PHP bibliotece GD, możliwa jest łatwa praca na grafice – od prostej obróbki po rysowanie...

Internet Maker

Symfony to jeden z najlepszych dostępnych obecnie frameworków w języku PHP. Dzięki jasnej strukturze oraz generatorom kodu przygotowanie kompletnej aplikacji WWW zajmuje kilku minut. Artykuł opisuje krok po kroku...