05-08.pdf

(323 KB) Pobierz
Sprawdzanie
poprawności składni
XML-a
5
Po lekturze poprzednich rozdziałów Czytelnik potrafi już stworzyć dokument XML, przetworzyć go
za pomocą klas SAX oraz zawęzić. W tym rozdziale zostanie omówione kolejne zagadnienie — spraw-
dzanie poprawności dokumentu XML za pomocą Javy. Bez takiej możliwości tworzenie aplikacji
firma-firma oraz komunikacji międzyaplikacyjnej staje się o wiele trudniejsze. Zawężenie zwię-
ksza przenośność danych; natomiast sprawdzanie poprawności — spójność. Innymi słowy, moż-
liwość zawężenia dokumentu nie zda się na wiele, jeśli stworzonych zawężeń nie przeforsujemy
w aplikacji XML.
W tym rozdziale przedstawione zostaną klasy i interfejsy SAX służące do sprawdzania poprawności
dokumentów XML względem ich zawężeń. Czytelnik dowie się, jak ustawić cechy i właściwości
parsera zgodnego z SAX, aby możliwe było proste sprawdzanie poprawności, obsługa przestrzeni
nazw i wykonywanie innych czynności. Ponadto szczegółowo omówione zostaną błędy i ostrzeże-
nia zgłaszane przez parsery sprawdzające poprawność.
Konfiguracja parsera
W obliczu bogactwa specyfikacji i technologii autorstwa konsorcjum W3C związanych z językiem
XML, dodanie obsługi nowej cechy lub właściwości nie jest proste. W wielu implementacjach par-
serów dodano własne rozszerzenia lub metody kosztem przenośności kodu. W pakietach tych mógł
zostać zaimplementowany interfejs
XMLReader,
ale metody do ustawiania sprawdzania popraw-
ności, obsługi przestrzeni nazw i innych kluczowych cech są odmienne w różnych implementa-
cjach parserów. W związku z tym w interfejsie SAX 2.0 zdefiniowano standardowy mechanizm do
ustawiania istotnych właściwości i cech parsera. Umożliwia on dodawanie nowych właściwości
i cech w miarę przyjmowania ich przez W3C, bez konieczności stosowania własnych metod lub
rozszerzeń.
C:\Users\a_czajka\Dropbox\Informatyka\książki
informatyczne\Java i XML\05-08.DOC
strona
117
118
Rozdział 5. Sprawdzanie poprawności składni XML-a
Ustawianie właściwości i cech
Na szczęście dla nas, interfejs SAX 2.0 wyposażono w metody służące do ustawiania właściwości
i cech interfejsu
XMLReader.
Oznacza to, że w celu zażądania sprawdzania poprawności, usta-
wienia separatora przestrzeni nazw czy obsługi innych cech nie trzeba zmieniać zbyt wiele w istnie-
jącym kodzie. Odpowiednie metody przedstawione są w tabeli 5.1.
Tabela 5.1. Metody do obsługi właściwości i cech parsera
Metoda
Zwraca
Parametry
String
IDwlasciwosci,
Object wartosc
String IDcechy,
boolean stan
String
IDwlasciwosci
String IDcechy
Składnia
parser.setProperty(„[URI
wlasciwosci]”, „[Parametr
obiektu]”);
parser.setFeature(„[URI cechy]”,
true);
String separator =
(Stringparser.getProperty(„[URI
wlasciwosci]”);
if (parser.getFeature(„[URI
cechy]”)) {robiCos();}
setProperty() void
setFeature()
void
getProperty() Object
getFeature()
boolean
W każdej z powyższych metod identyfikatorem ID określonej właściwości lub cechy jest identyfi-
kator URI. Lista najważniejszych właściwości i cech jest zamieszczona w dodatku B. Producent
parsera XML powinien udostępnić dodatkową dokumentację informujacą o obsługiwanych ce-
chach i właściwościach. Pamiętajmy jednak, że identyfikatory te, podobnie jak identyfikatory URI
przestrzeni nazw, służą wyłącznie do kojarzenia odpowiednich cech. Dobry parser umożliwi korzys-
tanie z nich bez posiadania połączenia z siecią. W tym sensie identyfikatory URI można postrzegać
jako proste stałe, które akurat mają format URI. Po skorzystaniu z takiej metody następuje lokalne
przetworzenie identyfikatora, często jako stałej reprezentującej odpowiednią czynność, którą na-
leży podjąć.
W kontekście konfiguracji parsera
właściwość
wymaga obecności obiektu, z którego można sko-
rzystać. Na przykład w celu obsługi leksykalnej jako wartość właściwości można dostarczyć klasę
LexicalHandler.
Cecha
natomiast ma postać znacznika wykorzystywanego przez parser
w celu określenia, czy ma nastąpić przetwarzanie określonego typu. Typowe cechy to sprawdzanie
poprawności, obsługa przestrzeni nazw i dołączanie encji zewnętrznych.
Najwygodniejsza własność powyższych metod polega na tym, że umożliwiają one łatwe dodawanie
i zmianę różnych cech. Nowe lub zaktualizowane właściwości wymagają obsługi ze strony parsera,
ale dostęp do nich uzyskuje się wciąż za pomocą tych samych metod — konieczne jest tylko zdefi-
niowanie nowego identyfikatora URI. Bez względu na złożoność nowych koncepcji związanych
z XML, ich implementacja w parserach przebiega bezproblemowo, właśnie dzięki tym metodom.
Włączanie sprawdzania poprawności
Czytelnik dowiedział się już, jak ustawiać cechy i właściwości, ale nie poznał jeszcze samych cech
i właściwości. W tym rozdziale interesujemy się przede wszystkim sprawdzaniem poprawności
w czasie przetwarzania. Aby zilustrować, jak ważne są wspomniane wyżej metody, trzeba odwo-
łać się do historii. W interfejsie SAX 1.0 implementacje parserów musiały udostępniać własne roz-
wiązania do obsługi przetwarzania ze sprawdzaniem poprawności lub bez. Nie było możliwości
C:\Users\a_czajka\Dropbox\Informatyka\książki
informatyczne\Java i XML\05-08.DOC
strona
118
Konfiguracja parsera
119
włączenia lub wyłączenia sprawdzania poprawności w sposób standardowy, więc najprostszy spo-
sób polegał na dostarczeniu dwóch niezależnych klas przetwarzających. Na przykład, aby wyko-
nać przetwarzanie bez sprawdzania poprawności we wczesnych wersjach parsera Project X firmy
Sun, trzeba było skorzystać z przedstawionego poniżej fragmentu kodu.
Przykład 5.1. Uruchamianie parsera nie sprawdzającego poprawności w interfejsie SAX 1.0
try {
// Rejestrujemy parser w SAX
Parser parser =
ParserFactory.makeParser(
"com.sun.xml.parser.Parser");
// Przetwarzamy dokument
parser.parse(uri);
} catch (Exception e) {
e.printStackTrace();
}
Ponieważ nie istniał standardowy mechanizm włączania sprawdzania poprawności, trzeba było
załadować inną klasę; ta nowa klasa jest niemal identyczną implementacją interfejsu
Parser
w SAX 1.0, ale wykonującą sprawdzanie poprawności. Kod pozwalający na użycie parsera jest
niemal identyczny (przykład 5.2), różnica jest tylko w klasie załadowanej w celu przetwarzania.
Przykład 5.2. Uruchamianie parsera sprawdzającego poprawność w interfejsie SAX 1.0
try {
// Rejestrujemy parser w SAX
Parser parser =
ParserFactory.makeParser(
"com.sun.xml.parser.ValidatingParser");
// Przetwarzamy dokument
parser.parse(uri);
} catch (Exception e) {
e.printStackTrace();
}
Włączając lub wyłączając sprawdzanie poprawności trzeba więc było zmieniać i kompilować kod.
Ponadto wynikał z tego dodatkowy problem z przetwarzaniem. Standardowe środowisko pro-
gramistyczne wykorzystuje kod, który sprawdza poprawność danych XML wytwarzanych przez
aplikację. To sprawdzanie poprawności, choć obniża wydajność, zapewnia, że aplikacja tworzy za-
wsze poprawny kod XML albo że otrzymuje poprawne dokumenty XML na wejściu. Często takie
zawężenia po intensywnym testowaniu można usunąć, dzięki czemu odzyskuje się wysoką wydaj-
ność działania aplikacji. Pozbawienie parsera możliwości sprawdzania poprawności jest uzasadnio-
ne, ponieważ dogłębne testy potwierdziły poprawność tworzonego dokumentu XML; ale zmiana ta
wymaga modyfikacji i rekompilacji kodu. Wydaje się to być sprawą banalną, ale wiele firm nie
pozwala na wdrożenie do produkcji kodu, który był modyfikowany później niż przed jakimś określo-
nym czasem — często kilka dni, a nawet tygodni. Taka niewielka zmiana może więc spowodować
dodatkowy cykl testowy — niejednokrotnie niepotrzebny, a dodatkowo wydłużający czas wdrożenie
aplikacji.
Ale przecież nazwę parsera można pobrać z pliku właściwości (zostało to już powiedziane w roz-
dziale 2. przy okazji opisywania aspektów przenośności aplikacji XML). Tak, ale zmiana całej
C:\Users\a_czajka\Dropbox\Informatyka\książki
informatyczne\Java i XML\05-08.DOC
strona
119
120
Rozdział 5. Sprawdzanie poprawności składni XML-a
implementacji parsera tuż przed wdrażaniem aplikacji do produkcji to zmiana poważna, która po-
winna być należycie przetestowana. Jeśli porównamy to ze zmianą wartości zestawu cech (zakła-
dając, że wartość do ustawiania tej cechy także pobierana jest z pliku właściwości), to łatwo zga-
dnąć, które rozwiązanie jest lepsze.
Z tych wszystkich powodów w interfejsie SAX 2.0 do
XMLReader
dodano omawiane metody. Dzięki
nim włączenie sprawdzania poprawności polega na wykorzystaniu odpowiedniego identyfikatora URI:
http://xml.org/sax/features/validation.
Moglibyśmy zażądać również przetwarzania encji zewnętrznych
i przestrzeni nazw, ale tymczasem „włączymy” tylko sprawdzanie poprawności (przykład 5.3).
Przykład 5.3. Włączanie sprawdzania poprawności
// Stwórz egzemplarze procedur obsługi
ContentHandler contentHandler = new MyContentHandler();
ErrorHandler errorHandler = new MyErrorHandler();
try {
// Stwórz egzemplarz parsera
XMLReader parser =
XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser");
// Zarejestruj procedurę obsługi zawartości
parser.setContentHandler(contentHandler);
// Zarejestruj procedurę obsługi błędów
parser.setErrorHandler(errorHandler);
parser.setFeature("http://xml.org/sax/features/validation",
true);
// Przetwórz dokument
parser.parse(uri);
} catch (IOException e) {
System.out.println("Błąd przy wczytywaniu URI: " +
e.getMessage());
} catch (SAXException e) {
System.out.println("Błąd w przetwarzaniu: " + e.getMessage());
}
Po tych prostych zmianach możemy już zmodyfikować nasz przykładowy plik XML tak, by znów
zawierał odwołanie do definicji DTD i zewnętrzną encję (zostały one opatrzone komentarzami w
poprzednim rozdziale).
<?xml version="1.0" encoding="ISO-8859-2"?>
<!-- Tego jeszcze nie potrzebujemy
<?xml-stylesheet href="XSL\JavaXML.html.xsl" type="text/xsl"?>
<?xml-stylesheet href="XSL\JavaXML.wml.xsl" type="text/xsl"
media="wap"?>
<?cocoon-process type="xslt"?>
-->
<!DOCTYPE JavaXML:Ksiazka SYSTEM "DTD\JavaXML.dtd">
<!-- Java i XML -->
<JavaXML:Ksiazka xmlns:JavaXML="http://www.oreilly.com/catalog/javaxml/">
<JavaXML:Tytul>Java i XML</JavaXML:Tytul>
C:\Users\a_czajka\Dropbox\Informatyka\książki
informatyczne\Java i XML\05-08.DOC
strona
120
Wynik sprawdzania poprawności
<JavaXML:Spis>
...
<!-- z encji także usuwamy komentarz -->
<JavaXML:Copyright>&OReillyCopyright;</JavaXML:Copyright>
121
Upewniamy się, czy w podanym tutaj katalogu mamy stworzoną wcześniej definicję DTD. Zanim
uruchomimy przykład, musimy połączyć się z Internetem — pamiętajmy, że encje muszą zostać
„przetłumaczone”. W naszym przykładowym pliku mamy taką encję —
OReillyCopyright.
W definicji DTD odwołujemy się do identyfikatora URI
http://www.oreilly.com/catalog/javaxml/
docs/copyright.xml.
Jeśli w czasie sprawdzania poprawności identyfikator URI nie jest dostępny,
pojawią się błędy przetwarzania. Jeśli nie mamy dostępu do Internetu albo nie chcemy z niego ko-
rzystać, możemy odwołanie sieciowe zastąpić odwołaniem do pliku lokalnego. Na przykład two-
rzymy jednowierszowy plik jak w przykładzie 5.4.
Przykład 5.4. Lokalny plik z opisem praw autorskich
Przykładowy współdzielony plik z opisem praw autorskich.
Plik ten zachowujemy w katalogu dostępnym z poziomu programu parsera i zamieniamy deklara-
cję encji w DTD na ścieżkę do tego pliku:
<!ENTITY OReillyCopyright SYSTEM
"encje/copyright.txt">
W tym przykładzie plik tekstowy zachowany jest pod nazwą
copyright.txt
w katalogu
encje/.
Po
takiej zmianie możemy już uruchomić nasz program na przykładowym pliku XML.
Wynik
sprawdzania poprawności
Sprawdzamy, czy pliki XML, DTD, plik z prawami autorskimi (o ile taki stworzyliśmy) oraz od-
powiednie klasy Javy znajdują się w jednym miejscu, i uruchamiamy przykładowy program. Wy-
nik może być zaskakujący (przykład 5.5).
Przykład 5.5. Wynik działania programu SAXParserDemo
D:\prod\JavaXML> java SAXParserDemo D:\prod\JavaXML\contents\contents.xml
Przetwarzanie pliku XML: contents.xml
* setDocumentLocator() została wywołana
Rozpoczyna się przetwarzanie...
**Przetwarzanie błędu**
Wiersz:
11
URI:
file:/D:/prod/JavaXML/contents/contents.xml
Komunikat: Document root element "JavaXML:Ksiazka", must match DOCTYPE
root "JavaXML:Ksiazka".
Ten dość enigmatyczny komunikat wynika z istnienia poważnego problemu związanego z używa-
niem definicji DTD i przestrzeni nazw. Z informacji wynikałoby, że element główny określony
w deklaracji
DOCTYPE
(JavaXML:Ksiazka) jest różny od elementu głównego samego doku-
mentu. Ale przecież elementy te są identyczne, prawda? Otóż nie! Domyślnie SAX 2.0 wymaga
C:\Users\a_czajka\Dropbox\Informatyka\książki
informatyczne\Java i XML\05-08.DOC
strona
121
Zgłoś jeśli naruszono regulamin