05 08 CTR4YEAEUMGQHN7FSNWCCAV4G Nieznany (2)

background image

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 117

5

Sprawdzanie

poprawności składni

XML-a

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ń.

background image

118

Rozdział 5. Sprawdzanie poprawności składni XML-a

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 118

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

Składnia

setProperty() void

String

IDwlasciwosci,

Object wartosc

parser.setProperty(„[URI

wlasciwosci]”, „[Parametr

obiektu]”);

setFeature()

void

String IDcechy,

boolean stan

parser.setFeature(„[URI cechy]”,

true);

getProperty() Object

String

IDwlasciwosci

String separator =

(Stringparser.getProperty(„[URI

wlasciwosci]”);

getFeature()

boolean

String IDcechy

if (parser.getFeature(„[URI

cechy]”)) {robiCos();}

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
włączenia lub wyłączenia sprawdzania poprawności w sposób standardowy, więc najprostszy spo-

background image

Konfiguracja parsera

119

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 119

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
implementacji parsera tuż przed wdrażaniem aplikacji do produkcji to zmiana poważna, która po-

background image

120

Rozdział 5. Sprawdzanie poprawności składni XML-a

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 120

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>

background image

Wynik sprawdzania poprawności

121

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 121

<JavaXML:Spis>

...

<!-- z encji także usuwamy komentarz -->
<JavaXML:Copyright>&OReillyCopyright;</JavaXML:Copyright>

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

background image

122

Rozdział 5. Sprawdzanie poprawności składni XML-a

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 122

od parserów włączenia przetwarzania przestrzeni nazw, chyba że jawnie wyłączymy tę cechę

1

. My

ustawienia domyślnego nie zmienialiśmy, a więc nasza implementacja interfejsu

XMLReader

jest

„świadoma” przestrzeni nazw. Nieoczekiwany rezultat wynika stąd, że nasz element główny jest
postrzegany (przez parser) jako

Ksiazka

z przedrostkiem przestrzeni nazw

JavaXML

. Ale

pamiętajmy, że XML 1.0 oraz definicje DTD nie rozróżniały pomiędzy przedrostkiem a nazwą
elementu, a więc element główny DTD oczekuje nazwy

JavaXML:Ksiazka

. Kiedy znajduje

Ksiazka

, zgłasza błąd.

Jedynym sposobem obejścia tej nieco uciążliwej cechy SAX-a jest wyłączenie „świadomości”
przestrzeni nazw w dokumentach, w których sprawdzanie poprawności odbywa się za pomocą
DTD. Dodajemy następujący fragment kodu:

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

// Włącz sprawdzanie poprawności
parser.setFeature("http://xml.org/sax/features/validation",
true);

// Wyłącz "świadomość" przestrzeni nazw
parser.setFeature("http://xml.org/sax/features/namespaces",
false);

// 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 tej zmianie wszystkie nazwy traktowane są jako zawierające zarówno przedrostek przestrzeni
nazw, jak i nazwę lokalną elementu. Do korzystania z przestrzeni nazw powrócimy w dalszej częś-
ci rozdziału. Tymczasem spróbujmy ponownie uruchomić program z zaimplementowanymi zmia-
nami. Powinniśmy uzyskać wynik widoczny w przykładzie 5.6.

Przykład 5.6. Wynik działania programu SAXParserDemo po wyłączeniu obsługi przestrzeni nazw

D:\prod\JavaXML> java SAXParserDemo D:\prod\JavaXML\contents\contents.xml

Przetwarzanie pliku XML: contents.xml

1

Domyślne włączanie obsługi przestrzeni nazw miało miejsce w czasie pisania tej książki, jednakże niektórzy programiści

związani z XML-em (np. zespół programistów Apache Xerces) postulowali wyłączenie tej funkcji. Jeśli Czytelnik nie
otrzymuje wspomnianego wyżej komunikatu, to być może domyślne ustawienie zostało już zmienione. (W czasie tłu-
maczenia książki, w grudniu 2000 r., obsługa przestrzeni nazw była już domyślnie wyłączona — przyp. tłum.).

background image

Wynik sprawdzania poprawności

123

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 123

* setDocumentLocator() została wywołana
Rozpoczyna się przetwarzanie...
startElement: nie posiada skojarzonej przestrzeni nazw
Atrybut: =http://www.oreilly.com/catalog/javaxml/
ignorableWhitespace: [
]
startElement: nie posiada skojarzonej przestrzeni nazw
znaki: Java i XML
endElement:

...

Wynik może nas nieco rozczarować. Nie różni się niczym od wyniku uzyskanego w przypadku
parsera nie sprawdzającego poprawności (zob. rozdział 3.)! To dlatego, że nasz dokument jest po-
prawny i poprawnie sformatowany — parser sprawdzający poprawność nie ma o czym komuniko-
wać. To ważne — zachowanie parsera w przypadku sprawdzania poprawnego dokumentu niemal
nie różni się od zachowania parsera nie sprawdzającego poprawności. Jeśli to wydaje się dziwne,
to należy przypomnieć, że sprawdzanie poprawności ma na celu zagwarantowanie, że dokument
nie łamie zdefiniowanych wcześniej zasad. Jeśli wszystkie reguły są przestrzegane, aplikacja użyje
dokumentu XML zgodnie z jego przeznaczeniem. Tylko kiedy nastąpi złamanie zasad, parser spraw-
dzający poprawność wkracza do akcji — poniżej zostaną omówione takie przypadki.

Wcześniej jednak trzeba zauważyć, że mimo wszystko powyższy wynik nieco różni się od po-
przednich. Wcześniej wszystkie znaki białe pomiędzy elementami w dokumencie XML zgłaszane
były poprzez wywołanie metody

characters()

w implementacji

ContentHandler

. W do-

kumencie XML, którego poprawność nie jest sprawdzana, parser ma możliwość zgłoszenia bia-
łych znaków albo przez tę metodę, albo przez

ignorableWhitespace()

.Wynika to z faktu,

że parser nie może zakładać określonego przeznaczenia obecności białych znaków pomiędzy ele-
mentami bez definicji DTD zawężającej XML. Kiedy w programie włączamy sprawdzanie popraw-
ności, widzimy, że wszystkie znaki białe zgłaszane są za pomocą

ignorableWhitespace()

.

Przy sprawdzaniu poprawności wszystkie białe znaki są ignorowane i traktowane tak, jakby ich
nie było — chyba że zostanie to inaczej ujęte w definicji DTD. Dzięki temu parser potrafi określić,
czy zawartość elementu XML zgodna jest z definicją DTD bez analizowania białych znaków ota-
czających tę zawartość. Innymi słowy, parser potraktuje taki fragment XML:

<dokument>
<element1>
<element2>Cześć!</element2>
</element1>
</dokument>

dokładnie tak samo jak:

<dokument><element1><element2>Cześć!</element2></element1></dokument>

Drugi fragment jest co prawda mniej miły dla ludzkiego oka, ale zgodny jest z tymi samymi za-
wężeniami co fragment pierwszy i powinien być identycznie traktowany w czasie sprawdzania
poprawności. Brak wcięć nie może wpłynąć na działanie aplikacji korzystających z danych XML.
Gdyby białe znaki tworzące wcięcia zgłaszane były poprzez wywołanie

characters()

, aplika-

cja monitorująca to wywołanie uznałaby, że dokumenty nie są identyczne.

Ostrzeżenia

W wyniku żądania sprawdzania poprawności nie mogą się pojawić niemal żadne ostrzeżenia. Ja-
kiekolwiek dane XML niezgodne z odpowiednią definicją DTD są postrzegane jako błąd. Niepo-

background image

124

Rozdział 5. Sprawdzanie poprawności składni XML-a

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 124

prawność dokumentu została uznana przez W3C za rzecz na tyle ważną, że zawsze zgłaszany jest
wtedy błąd. Dlatego trudno jest, szczególnie za pomocą parsera SAX 2.0, wywołać ostrzeżenie.
Jednak istnieją parsery SAX 1.0, w których jest to możliwe. Na przykład parser Project X firmy
Sun zawiera obecnie klasę służącą do sprawdzania poprawności XML-a w czasie przetwarzania
i klasę nie wykonującą takiego sprawdzania. Jest to rozwiązanie podobne do omawianego wcześ-
niej. Jeśli parser sprawdzający poprawność przetwarza dokument nie deklarujący definicji DTD
w sposób jawny, generowane jest ostrzeżenie. Dotyczy to tylko niektórych implementacji parse-
rów SAX 1.0, dlatego w niniejszej książce nie zostanie przedstawiony kod powodujący zgłoszenie
ostrzeżenia. Czytelnik może natomiast obejrzeć wynik działania takiego ostrzeżenia (przykład 5.7)
— podobny do tych opisywanych w rozdziale 3.

Przykład 5.7. Program SAXParserDemo zgłaszający ostrzeżenie

D:\prod\JavaXML&gt; java SAXParserDemo D:\prod\JavaXML\contents\contents.xml

Przetwarzanie pliku XML: contents.xml

* setDocumentLocator() została wywołana
Rozpoczyna się przetwarzanie...
**Przetwarzanie ostrzeżenia**
Wiersz: 6
URI: file:/D:/prod/JavaXML/contents/contents.xml
Komunikat: Valid documents must have a <!DOCTYPE declaration.

Taki komunikat pojawiłby się, gdyby konstrukcja

<!DOCTYPE>

była opatrzona komentarzami

lub gdyby została pominięta. Ponieważ niemal wszystkie parsery XML (w tym Project X firmy Sun)
ewoluują w kierunku zgodności z SAX 2.0, takiego ostrzeżenia najprawdopodobniej nigdy nie
zobaczymy.

Błędy niekrytyczne

Najbardziej typowym problemem z interfejsem SAX, jaki będzie zgłaszany w czasie sprawdzania
poprawności XML, jest błąd o statusie „niekrytyczny”. Błąd taki generowany jest za każdym ra-
zem, gdy pogwałcone zostaną zawężenia XML. Aby to zademonstrować, wykonajmy następującą
zmianę w naszym przykładowym dokumencie XML (czyniąc go niepoprawnym):

<?xml version="1.0" encoding="ISO-8859-2"?>

<!-- to jeszcze nie jest potrzebne
<?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/"

dataPublikacji="czerwiec 2000">
<JavaXML:Tytul>Java i XML</JavaXML:Tytul>
<JavaXML:Spis>

Taka zmiana, choć nie wpływa na poprawność danych XML, nie jest zgodna z narzuconymi regu-
łami — atrybut

dataPublikacji

nie został zadeklarowany w definicji DTD i w czasie prze-

twarzania zgłoszony zostanie błąd (przykład 5.8).

background image

Wynik sprawdzania poprawności

125

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 125

Przykład 5.8. Błąd zgłaszany przez program SAXParserDemo

D:\prod\JavaXML&gt; 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: 14
URI:

file:///mnt/teksty_zdalny/biura/helion/javaxml/cwiczenia/contents.xml

Komunikat: Attribute "dataPublikacji" must be declared for element type
"JavaXML:Ksiazka".
Błąd w przetwarzaniu: Napotkano błąd

Analizator przesyła błąd do implementacji

ErrorHandler

, która zgłasza błąd — w tym przy-

padku mówiący, że atrybut nie został zadeklarowany dla zamykającego go elementu. Oczywiście,
poprawność dokumentu można zburzyć na wiele sposobów — to tylko jeden z nich; zawsze je-
dnak zgłoszony zostanie ten sam błąd, warto więc poeksperymentować. Należy po prostu pamię-
tać, że jakiekolwiek pogwałcenie DTD spowoduje zgłoszenie błędu. Dotyczy to przypadków,
w których zawartość dokumentu nie jest poprawna, elementy nie są prawidłowo zagnieżdżone,
atrybuty znajdują się nie tam, gdzie trzeba, lub są niepoprawne itd.

Błędy krytyczne

Co ciekawe, dokument niezgodny z zawężeniami DTD nigdy nie spowoduje wygenerowania błę-
du krytycznego. W czasie przetwarzania dokumentu nie mogą zajść takie warunki, które spowodu-
ją przerwanie tego procesu. Może nam się wydawać, że kontynuacja przetwarzania niepoprawnego
dokumentu kłóci się z celowością sprawdzania poprawności. Należy jednak pamiętać o tym, że
w większości przypadków dokumenty XML są generowane przez aplikacje. Innymi słowy, aplikacja
otrzymuje dane wejściowe XML z innego programu lub podprogramu. Jeśli dane te są niepopraw-
ne, aplikacja usiłująca z nich skorzystać powinna zgłosić aplikacji klienta błąd, a nie przerywać
przetwarzanie. Co więcej, przetwarzanie to niejednokrotnie powinno być kontynuowane, aby możli-
we było zakończenie procesu w sposób elegancki — w ten sposób aplikacja jest w stanie precyzyjnie
zakomunikować, jakie błędy wystąpiły. Dokumenty błędnie sformatowane spowodują zatrzymanie
przetwarzania, a dokumenty niepoprawne wskażą, że albo nastąpił błąd, który można poprawić, albo
zaszła sytuacja, o której klient powinien wiedzieć, np. wprowadzono niepoprawne dane. Zasta-
nówmy się, jak trudno byłoby obsługiwać edytor XML lub środowisko programistyczne IDE, jeśli
za każdym razem, gdy nie do końca przestrzegaliśmy zawężeń DTD, edytor kończyłby działanie
z błędem krytycznym, lub też odmawiałby w ogóle przetwarzania dokumentu; a przecież niektóre
edytory mogłyby takie błędy poprawiać za nas! Dlatego właśnie niepoprawne dokumenty powodu-
ją zgłaszanie ostrzeżeń i błędów, ale nigdy błędów krytycznych.

Jedyny błąd krytyczny, na jaki możemy się natknąć przy używaniu definicji DTD w dokumentach,
których poprawność nie jest sprawdzana, to błąd składniowy w wykorzystywanej definicji DTD.
To nie powinno Czytelnika dziwić, jako że błędy składniowe w dokumencie XML także powodują
błędy krytyczne. Ma tutaj miejsce to samo rozumowanie — nie jest możliwe dalsze przetwarzanie
lub sprawdzanie poprawności, jeśli nie można określić zawężeń (a tak jest, gdy składnia DTD nie
jest poprawna). Powinniśmy zdawać sobie sprawę, że to nie to samo co błąd generowany, gdy ma-
my do czynienia z niepoprawnie sformatowanym dokumentem XML — główna różnica polega na

background image

126

Rozdział 5. Sprawdzanie poprawności składni XML-a

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 126

tym, że definicja DTD nigdy nie jest postrzegana jako „poprawnie sformatowana”, bo przecież nie
jest to tak naprawdę dokument XML. Ale rezultat wystąpienia błędów składniowych w DTD jest
taki sam, jak rezultat przetwarzania niepoprawnie sformatowanych danych XML.

Interfejs DTDHandler

Ostatnia ważna procedura obsługi udostępniania przez SAX rejestruje metody wywołań wstecz-
nych w czasie procesu czytania i przetwarzania definicji DTD dokumentu. W interfejsie tym nie
zdefiniowane zdarzenia, jakie występują w czasie sprawdzania poprawności, a jedynie te, które
występują w czasie czytania DTD. W podrozdziale dotyczącym pułapek zostaną omówione pro-
blemy wywoływane przez to zróżnicowanie. Ta procedura obsługi zachowuje się w taki sam sposób
jak interfejsy

ContentHandler

i

ErrorHandler

, omawiane w rozdziale 3. — definiowane

są dwie metody wywoływane w czasie przetwarzania.

Sprawdzanie poprawności dokumentu XML jest bardzo ważne, ale zdarzenia związane z odczyty-
waniem dokumentu DTD — już mniej. Są tylko dwie metody, obie nie są wykorzystywane tak
często, a więc prawdopodobnie rzadko Czytelnik będzie musiał korzystać z interfejsu

DTDHand-

ler

(chyba że pisząc edytor lub IDE dla XML-a będzie chciał tworzyć lub przetwarzać dokumen-

ty DTD w celu sprawdzenia poprawności zapisu i składni). Dlatego pokrótce omówimy tutaj te
dwie metody udostępniane przez SAX, ale nie będziemy poświęcali im zbyt dużo czasu. O opcjo-
nalnej procedurze obsługi SAX, służącej do pobierania innych informacji z DTD, można przeczy-
tać w części

DeclHandler

, w dodatku A (pakiet

org.xml.sax.ext

).

Nieprzetwarzane deklaracje encji

Pierwsze wywołanie wsteczne,

unparsedEntityDecl()

, wywoływane jest, kiedy deklaracja

encji w DTD mówi, iż encja ta ma nie być przetwarzana przez parser XML. Choć nie omawialiś-
my jeszcze przykładu takiego zachowania, nieprzetwarzane encje są dość typowe w dokumentach
XML, w których istnieją odwołania do plików graficznych lub innych danych binarnych (często
plików multimedialnych). Metoda ta pobiera nazwę encji, identyfikatory, publiczny i systemowy,
oraz nazwę notacyjną encji. Nazwy notacyjne w XML-u nie były jeszcze omawiane. Spójrzmy na
przykład dokumentu XML odwołującego się do pliku graficznego (np. do logo firmy) — przykład 5.9.

Przykład 5.9. Dokument XML z nieprzetwarzaną encją

<dokument>
<mojeLogo>&LogoFirmy;</mojeLogo>
</dokument>

Podczas przetwarzania parser stara się przetłumaczyć wszystkie encje i wstawić w ich miejsce
przetworzoną wartość. Jednakże parser nie potrafi przetwarzać plików graficznych i powinien
pozostawić dane binarne w postaci nieprzetworzonej. Można go o tym „poinformować” poprzez
następującą definicję typu dokumentu:

<!ENTITY LogoFirmy SYSTEM "obrazki/logo.gif" NDATA gif>

Słowo kluczowe NDATA powoduje, że parser XML nie przetworzy tej encji. Gdyby ta definicja
DTD była przetwarzana za pośrednictwem zarejestrowanej implementacji

DTDHandler

, infor-

macja w deklaracji encji zostałaby przekazana do wywołania. Inną ważną sprawą jest tutaj fakt, że
wywołanie następuje w miejscu deklaracji w DTD, a nie w chwili, gdy deklaracja ta jest prze-

twarzana w dokumencie XML. Oznacza to, że nawet jeśli encja nie znajduje się w dokumencie

background image

Interfejs DTDHandler

127

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 127

XML, to i tak wywołanie nastąpi. To ma sens — przecież wywołanie to stanowi część interfejsu

DTDHandler

, a nie

ContentHandler

.

Deklaracje notacji

Deklaracje notacji zawsze skojarzone są z nieprzetwarzanymi deklaracjami i stanowią przedmiot
działania drugiej metody wchodzącej w skład

DTDHandler

. Ostatnia część powyższej deklaracji

nieprzetwarzanej encji to słowo „gif”. Słowo to określa typ nieprzetwarzanej encji i musi stanowić
odwołanie do typu zdefiniowanego w innym miejscu DTD, za pośrednictwem konstrukcji

NOTA-

TION

. W ten sposób parser poznaje identyfikator URI dla danego typu — często jest to odwołanie

publiczne dla typów binarnych. Przypomina to odwoływanie się do DTD z dokumentu XML,
ponieważ specyficzne dane (w tym przypadku plik GIF) są kojarzone z publicznym identyfikato-
rem lub URI. Przetwarzanie definicji

NOTATION

i deklaracji nieprzetwarzanej encji powoduje

w rezultacie, że parser XML nie przetwarza danych binarnych. A o to właśnie chodziło.

<!NOTATION gif SYSTEM "http://www.gif.com">

Wystąpienia takich deklaracji zgłaszane są zarejestrowanej procedurze obsługi poprzez wywołanie

notationDecl()

. Po wywołaniu metoda ta otrzymuje deklarację notacji, identyfikator syste-

mowy i dostępny identyfikator publiczny. Podobnie jak w przypadku nieprzetwarzanych encji,
metoda ta wywoływana jest podczas odczytywania definicji DTD, a nie samego dokumentu XML.

Rejestrowanie procedury obsługi

Rejestrowanie implementacji

DTDHandler

w naszym parserze XML nie różni się niczym od re-

jestrowania procedur obsługi błędów i zawartości. Egzemplarz klasy przekazywany jest metodzie

setDTDHandler()

, a parser rejestruje odpowiednie zdarzenia związane z metodami SAX w tej

klasie:

import java.io.IOException;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;

import org.xml.sax.DTDHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

...

// Stwórz egzemplarze procedur obsługi
ContentHandler contentHandler = new MyContentHandler();
ErrorHandler errorHandler = new MyErrorHandler();

DTDHandler dtdHandler = new MyDTDHandler();

try {
// Stwórz egzemplarz parsera
XMLReader parser =
XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser");

// Zarejestruj procedurę obsługi zawartości
parser.setContentHandler(contentHandler);

background image

128

Rozdział 5. Sprawdzanie poprawności składni XML-a

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 128

// Zarejestruj procedurę obsługi błędów
parser.setErrorHandler(errorHandler);

// Zarejestruj procedurę obsługi DTD
parser.setDTDHandler(dtdHandler);

// Włącz sprawdzanie poprawności
parser.setFeature("http://xml.org/sax/features/validation",
true);

// Wyłącz "świadomość" przestrzeni nazw
parser.setFeature("http://xml.org/sax/features/namespaces",
false);

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

I cała reszta...

Czytelnik może odnieść wrażenie, że interfejs procedury obsługi DTD został omówiony dość po-
bieżnie, szczególnie w porównaniu z innymi procedurami. Jednakże aplikacje wykorzystujące XML
rzadko muszą rejestrować tego typu procedury. W systemach XML-owych często sprawdza się
poprawność dokumentu, ale szczegółowe informacje odnośnie nieprzetworzonych encji rzadko
przydają się na wyższym poziomie wykorzystania XML-a. Dlatego właśnie przejdziemy do innych
szczegółowych informacji, bardziej przydatnych przy tworzeniu aplikacji korzystających z XML-a.

Czytelnik mógłby oczekiwać, że teraz zostanie omówione sprawdzanie poprawności za pomocą
XML Schema; schematy są jednakże w coraz większym stopniu wykorzystywane nie tylko do
sprawdzania poprawności, ale także do zwykłej reprezentacji danych. Dlatego właśnie omawianie
schematów z poziomu Javy zostało przeniesione do rozdziału 14. — wtedy Czytelnik będzie już
znał XSL i różnego rodzaju praktyczne sposoby użycia XML-a w aplikacjach. Interfejsy SAX do
obsługi schematów są o wiele potężniejsze niż

DTDHandler

i będą wymagały od Czytelnika

większej wiedzy z zakresu XML-a. W niniejszym rozdziale omawianie sprawdzania poprawności
danych XML zostanie zakończone przedstawieniem typowych problemów, na jakie można się nat-
knąć w czasie tego procesu.

Uwaga! Pułapka!

Podczas sprawdzania poprawności dokumentu XML na programistę czyha wiele pułapek. Poniżej
zostaną omówione problemy, z którymi najczęściej mają do czynienia początkujący programiści
XML, a także te, których rozwiązanie wcale nie jest łatwe. Warto dobrze się zapoznać z poniż-
szym materiałem, bo opisywane trudności spędziły sen z oczu już niejednemu programiście.

background image

Uwaga! Pułapka!

129

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 129

Obsługa sprawdzania poprawności a obsługa definicji DTD

Jednym z typowych nieporozumień odnośnie SAX-a i sprawdzania poprawności jest błędne zało-
żenie, że sprawdzanie poprawności dokumentu XML następowało automatycznie po zarejestrowaniu
implementacji

DTDHandler

w parserze XML. Często zdarza się, że programista implementuje

ten interfejs i rejestruje go w parserze, ale nie włącza funkcji sprawdzania poprawności parsera.
Ten błąd wynika z zagubienia różnicy pomiędzy obsługą samego DTD a wykorzystaniem DTD do
sprawdzania poprawności. W tym przypadku definicja DTD zostanie przetworzona, wystąpią też
wszystkie wywołania związane z DTD (o ile są konieczne), jednakże nie nastąpi sprawdzenie
poprawności dokumentu XML — zostanie on wyłącznie przetworzony. Należy pamiętać, że
wynik sprawdzania poprawności poprawnego dokumentu XML wygląda niemal tak samo jak
wynik przetwarzania dokumentu bez sprawdzania jego poprawności; zawsze należy śledzić, kiedy
występuje sprawdzanie poprawności — w ten sposób można uniknąć błędów w aplikacji:

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

// Z tego nie wynika włączenie sprawdzania poprawności
parser.setDTDHandler(dtdHandler);

// Włącz sprawdzanie poprawności
parser.setFeature("http://xml.org/sax/features/validation",
true);

// Wyłącz "świadomość" przestrzeni nazw
parser.setFeature("http://xml.org/sax/features/namespaces",
false);

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

Trzeba koniecznie pamiętać o tym, że zarejestrowanie DTD nie ma nic wspólnego z procesem
sprawdzania poprawności; parser z zarejestrowaną implementacją

DTDHandler

nie zawsze spra-

wdza poprawność XML-a; zaś parser bez zarejestrowanej tej procedury może sprawdzić popraw-
ność. To od cech (w implementacjach SAX 2.0) lub klasy (w implementacjach sprzed SAX 2.0)
parsera XML zależy, czy sprawdzanie poprawności będzie się odbywało — a nie od zarejestrowa-
nej procedury obsługi. Pamiętajmy o tym — implementacja

DTDHandler

do sprawdzania po-

prawności nie jest potrzebna; potrzebne jest natomiast ustawienie odpowiednich cech parsera lub
użycie innej klasy.

background image

130

Rozdział 5. Sprawdzanie poprawności składni XML-a

C:\WINDOWS\Pulpit\Szymon\Java i XML\05-08.doc — strona 130

W czasie tworzenia priorytetem jest poprawność;

w gotowej aplikacji liczy się szybkość

Programista powinien wiedzieć, kiedy należy korzystać ze sprawdzania poprawności, a kiedy nie.
Jednym z największych problemów związanych ze sprawdzaniem poprawności jest często niezwykle
powolne działanie aplikacji produkcyjnej. Programista analizuje przyczyny takiego spadku wydaj-
ności pracy aplikacji, nie pamiętając o tym, że w środowisku produkcyjnym pozostawił włączone
sprawdzanie poprawności. Zazwyczaj sprawdzanie poprawności powinno mieć miejsce tylko w cza-
sie testowania lub w czasie działania procesu kontrolowania jakości (ang. Quality Assurance). Jeśli
część aplikacji generuje lub modyfikuje pliki XML, sprawdzanie poprawności wynikowego doku-
mentu XML powinno odbywać się właśnie w czasie testowania. To proces kosztowny — parser
przetwarza o wiele więcej danych i musi podjąć więcej decyzji dotyczących dokumentu.

Po sprawdzeniu poprawności wyników generowanych przez aplikację zazwyczaj dobrze jest wy-
łączyć sprawdzanie poprawności. Aplikacja produkcyjna zyskuje dzięki temu na wydajności, a jeśli
testowanie przeprowadzono zgodnie z zasadami tej sztuki, to nie powinny pojawić się żadne
problemy. Jedyną sytuacją, w której nie należy wyłączać sprawdzania poprawności w aplikacji
produkcyjnej, jest ta, w której w tworzeniu danych XML bierze aktywny udział klient — np. w na-
rzędziach XML IDE lub GUI, albo ta, w której dane XML uzyskuje się od innych aplikacji — np.
w e-biznesie. Wynika to stąd, że dane wejściowe są poza naszą kontrolą i mogą być niepoprawne
— sprawdzanie poprawności jest więc zalecane, a nawet oczekiwane przez klienta. Jednak w wię-
kszości przypadków gotowa aplikacja może pracować bez sprawdzania poprawności.

Co dalej?

Czytelnik powinien już dobrze znać zasady tworzenia dokumentów XML oraz sposoby ich zawę-
żania. W niniejszym rozdziale zostały omówione wszystkie najważniejsze aspekty korzystania
z interfejsów i klas SAX. Czytelnik poznał już cykle przetwarzania i sprawdzania poprawności
oraz dostępne wywołania. Powinien potrafić skonfigurować i użyć parsera XML oraz zarejestro-
wać w nim różne procedury obsługi SAX. W następnym rozdziale zostanie przedstawiona kolejna
specyfikacja — XSL, czyli rozszerzalny język arkuszy stylów. Omówienie przekształcania za po-
mocą XSL to wstęp do dyskusji o obiektowym modelu dokumentu i mechanizmach
publikacyjnych, a także do bardziej dogłębnego poznawania zagadnień programowania aplikacji.


Wyszukiwarka

Podobne podstrony:
2015 04 09 08 25 05 01id 28644 Nieznany (2)
2013 05 08 Pod Odpowid 28274 Nieznany (2)
2015 04 09 08 25 05 01id 28644 Nieznany (2)
2011 03 05 21;05;08
05 Majid 5744 Nieznany (2)
cw PAiTS 05 id 122324 Nieznany
08 2HBZ25RJBN6D4ITZUMZK4SZDSTJD Nieznany
NAI2006 05 id 313056 Nieznany
05 Spoinyid 5835 Nieznany
05 08
matma dyskretna 05 id 287941 Nieznany
cwiczenie 05 id 125057 Nieznany
05 Wykonywanie, odczytywanie i Nieznany
05 Occhid 5768 Nieznany (2)
714[01] Z1 05 Malowanie farba e Nieznany
05 TSid 5852 Nieznany (2)
05 Wodaid 5865 Nieznany (2)
pp test odp zima 05 air boratyn Nieznany

więcej podobnych podstron