Pracując z zewnętrznymi danymi zdarza nam się stanąć przed kwestią sprawdzenia poprawności danych, które do nas spływają w postaci xml. Czasami też możemy chcieć sprawdzić poprawność wygenerowanego przez nas xml’a, w szczególności czy zawiera wszelkie wymagane elementy.
Z pomocą wówczas przychodzi nam klasa DOMDocument oraz schemat opisu struktury xml w postaci DTD (Document Type Definition).
Na początek DTD
DTD pozwoli nam określić strukturę dokumentu (w naszym przypadku dokumentu xml) i listę poprawnych elementów. Samo DTD możemy zdefiniować bezpośrednio w dokumencie xml, jednak na potrzeby wymiany danych bardziej eleganckim rozwiązaniem będzie skorzystanie z DTD zdefiniowanego w oddzielnym pliku. Jeżeli definiujemy DTD w oddzielnym pliku, wówczas musimy w pliku xml przekazać informację o tym DTD, co wykonywane jest poprzez dodanie nastepującej informacji w pliku xml:
<!DOCTYPE root-element SYSTEM "dtd_filename">
gdzie:
- root-element – wskazuje na główny węzeł w naszym pliku xml
- dtd_filename – wskazuje na położenie pliku DTD, który ma zostać użyty do walidacji danych
Budowa pliku DTD
Na początku musimy zdefiniować elementy, które mają znaleźć się w pliku xml. Można to zrobić na dwa sposoby:
<!ELEMENT element-name type> <!ELEMENT element-name (subelements)>
Pierwszy sposób pozwala nam zdefiniować prosty element xml, który będzie zawierał po prostu wartość. Możemy określić jakiego typu ma być wartość elementu i w tym celu wykorzystywane są następujące operatory:
- EMPTY – dla elementów, które nie zawierają danych
- #PCDATA – dla danych znakowych
- ANY – dla dowolnych elementów
Drugi natomiast to definicja elementu, który będzie zawierał podelementy. W takiej sytuacji w nawiasie wymieniamy nazwy podelementów, które mają się tam znaleźć. Dla każdej z nazw podelementów możemy uzyć modyfikatorów takich jak przy wyrażeniach regularnych, które określą ilość tych elementów, czyli:
- + – jedno lub więcej wystąpień
- * – zero lub więcej wystąpień
- ? – zero lub jedno wystąpienie
- niezastosowanie modyfikatora jest tożsame z jednokrotnym użyciem podelementu
Kolejny krok to zdefiniowanie atrybutów elementów jeżeli takowe wystąpują. W tym celu wykorzystujemy następującą składnię:
<!ATTLIST element-name attribute-name attribute-type default-value>
Poszczególne elementy to:
- element-name – nazwa elementu, dla którego definiujemy atrybut
- attribute-name – nazwa atrybutu
- attribute-type – typ atrybutu (szczegółowy opis niżej)
- default-value – określa domyślną wartość dla elementu
Typy atrybutu mogą przyjmować następujące wartości:
- CDATA – dla danych znakowych
- (val1|val2|val3|…) – dla danych, które mają zostać wybrane z określonej listy dostępnych wartości
- ID – dla wartości, która jest unikalnym identyfikatorem
- IDREF – dla wartości, która jest referencją dla innego elementu
- IDREFS – dla wartości, która jest listą identyfikatorów innych elementów
Natomiast pole default-value może być przedstawiona w jednej z poniższych postaci:
- #REQUIRED – wartość atrybutu jest wymagana
- #IMPLIED – wartość atrybuty nie jest wymagana
- #FIXED value – wartość atrybutu ustawiona jest na stałe, a jej zmiana spowoduje wystąpienie błędu
- value – domyślna wartość
Przykład pliku XML
Plik example.xml
<?xml version="1.0"?> <!DOCTYPE books SYSTEM "example.dtd"> <books> <book> <author country="USA">Mario Puzo</author> <title>The Godfather</title> </book> <book> <author country="Poland">Szymborska Wisława</author> <title>Wiersze wybrane</title> </book> </books>
Przykład pliku DTD
Plik example.dtd
<!ELEMENT books (book+)> <!ELEMENT book (author, title)> <!ELEMENT author (#PCDATA)> <!ATTLIST author country CDATA #REQUIRED> <!ELEMENT title (#PCDATA)>
Walidacja przy użyciu DOMDocument
Do walidacji poprawności xml zarówno składniowej jak również pod kątem poprawności poszczególnych elementów pliku xml możemy wykorzystać poniższy kod:
libxml_use_internal_errors( true ); $dom = new DOMDocument(); $dom->load( "filename.xml" ); $dom->validate(); $errors = libxml_get_errors();
Pierwszym krokiem jest wyłączenie wewnętrznego raportowania błędów przez libxml i pozwolenie użytkownikowi na ich przechwycenie. Następnie tworzymy sobie instancję DOMDocument, do której ładujemy plik o wskazanej nazwie. Na instancji tego obiektu wywołujemy metodę validate(), która dokonuje walidacji pliku.
Ostatni krok to przechwycenie znalezionych błędów, co wykonujemy przy użyciu funkcji libxml_get_errors(). Funkcja ta zwróci nam pustą tablicę w przypadku braku błędów lub tablicę zawierającą elementy typu libXMLError w przypadku wystąpienia błędów walidacji.
Należy zwrócić uwage tylko na jedną kwestię. Jeżeli plik xml nie jest poprawny pod kątem składniowym, wówczas walidacji przy użyciu DTD jest pomijana. Tak więc odbywa się to niejako dwuetapowo: w pierwszym kroku sprawdzana jest poprawność składniowa pliku, a następnie jego walidacja w odniesieniu do wskazanego pliku DTD.
JA osobiście wole xml schema, lepiej mi się w nim pisze i jak pamietam ma troche więcej możliwości
Przydało się, dzięki!