XML

Autor: Marcin Kasiński
21.01.2011 13:24:00 +0200

XML, Extensible Markup Language, to metajęzyk, w którym nie sprecyzowano ani zestawu znaczników ani jego gramatyki. W swojej budowie przypomina on język HTML służący do budowy stron internetowych jednak czemu innemu ma on służyć i inne jego cechy spowodowały jego popularność. Mówiąc, że XML nie ma sprecyzowanego zestawu znaczników ani gramatykę chciałem zwrócić uwagę właśnie na jego uniwersalność. Stanowi on pewnego rodzaju szkielet z opisanymi zasadami jakimi ma się rządzić, ale w taki sposób aby go nie ograniczać i aby nie stał się standardem wykorzystywanym w niszowych rozwiązaniach. Tak jak język HTML posiada on znaczniki i ewentualne możliwe atrybuty tych znaczników. Zasadniczą różnicą jest to, że kiedy w przypadku HTML mamy z góry zdefiniowaną listę znaczników, np., <BODY>, czy też <TABLE>, w przypadku XML mamy pełną dowolność w nazewnictwie znaczników, np. <FAKTURA>, <NAZWISKO> . Drugą różnicą jest to, że w przypadku HTML każdy znacznik jest tak samo interpretowany i może zawierać elementy prezentacji, jak i konkretnych danych. W przypadku XML zawiera on znaczniki opisujące tylko i wyłącznie dane i jak jest on interpretowany zależy od systemów wymieniających pomiędzy sobą dane w postaci XML. Zbiega się to z postulatem odseparowywania w aplikacjach warstwy prezentacji od danych. Przykładowe dane XML mogą mieć postać:

<faktura>
	<numer>123</numer>
	<data>01.01.2005</data>
	<nabywca>
		<imie_nazwisko>Jan Kowalski</imie_nazwisko>
		<ulica>Prosta 8</ulica>
		<miasto>Warszawa</miasto>
		<kod>91-000</kod>
	</nabywca>
	<pozycje_faktury>
		<pozycja id='324'>
		<nazwa>zegarek</nazwa>
		<ilosc>2</ilosc>
		<cena>250</cena>
		</pozycja>
		...
	</pozycje_faktury>
</faktura>

W przypadku XML każdy znacznik otwierający musi mieć odpowiadający znacznik zamykający, znaczniki mogą być zagnieżdżone (zawierać wewnątrz siebie inne znaczniki) lecz nie mogą być "skrosowane" jak poniżej

<a><b></a></b>

Dokument XML musi mieć tylko i wyłącznie jeden główny znacznik, natomiast może zawierać nagłówkowe znaczniki META opisujące dokument takie jak strona kodowa itp.

DTD

Niekiedy ta dowolność standardu XML stanowi pewnego rodzaju ograniczenie i wskazane jest aby bardziej szczegółowo zdefiniować co może zawierać dokument XML. Do tego celu służy DTD (document type definition) czyli definicja typu dokumentu. Definicja DTD stanowi odrębny plik, do którego następuje wskazanie w dokumencie XML lub stanowi część dokumentu XML. Za jego pomocą można określić, jakie znaczniki mogą się pojawić w dokumencie, czy też jaką wartość może mieć dany atrybut. Aby poinformować parser XML, że plik powinien być zwalidowany pod kątem zgodności z defnicją znajdującą się w zewnętrznym pliku w początkowej fazie dokumentu XML należy umieścić znacznik:

<!DOCTYPE glowny_tag_XML SYSTEM  plik_z_definicją .dtd>

Przykładowy plik DTD może mieć postać:


<!ELEMENT product-catalog (product+)> 
<!ELEMENT product (description+, price+)>
	<!ATTLIST product 
		sku ID #REQUIRED 
		name CDATA #REQUIRED
	>
<!ELEMENT description (#PCDATA)> 
	<!ATTLIST description 
	locale CDATA #REQUIRED
	>
<!ELEMENT price (#PCDATA)>
	<!ATTLIST price 
		locale CDATA #REQUIRED 
		unit CDATA #REQUIRED
	>

Zapis ten oznacza że:

  • znacznik product-catalog zawiera jedno lub więcej wystąpień znacznika product,
  • znacznik product zawiera jedno lub więcej wystąpień znacznika description oraz jedno lub, więcej wystąpień znacznika price,
  • każdy znacznik product musi zawierać atrybut sku i name,
  • każdy znacznik description musi zawierać atrybut locale,
  • każdy znacznik price musi zawierać atrybut locale i unit,

Dla tak przygotowanego pliku definicji przykładowy plik XML może mieć postać:

<?xml version="1.0"?>
<!DOCTYPE product-catalog SYSTEM  product-catalog.dtd>
<product-catalog>
	<product sku="123456" name="The Product">
		<description locale="en_US">
			An excellent product.
		</description>
		<description locale="es_MX">
			Un producto excellente.
		</description>
		<price locale="en_US" unit="USD"> 99.95 </price>
		<price locale="es_MX" unit="MXP"> 9999.95 </price>
	 </product>
</product-catalog>

XML Schema

Podczas używania do walidacji dokumentów DTD można szybko zauważyć jego ograniczenia. Kiedy pojawiają się bardziej skomplikowane zależności pomiędzy elementami w dokumencie okazuje się, że standard DTD nie wystarcza. Do tego celu służy właśnie standard XML Schema. Rozwija on możliwości DTD i w zasadzie wypiera go z użycia pozwalając na dziedziczenie ograniczeń i zastosowanie skomplikowanych reguł weryfikacji dokumentu. Bardzo precyzyjnie można w nim określi ograniczenia zarówno dla typów prostych, jak i złożonych. Standardowo XML Schema ma w sobie wbudowane podstawowe typy danych jakie mogą pojawić się w dokumencie, takie jak xsd:decimal, xsd:string, itp. oraz pozwala na tworzenie rozbudowanych typów danych, w skład których wchodzą typy proste.

Przykładowy plik XSD dla dokumentu product-catalog może mieć postać:


<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<xsd:element type="product-catalog"/>
	<xsd:complexType name="productCatalog">
		<xsd:element type="productType" minOccurs="1"/>
	</xsd:complexType>
	<xsd:complexType name="productType">
		<xsd:element name="description" type="xsd:string" 
		minOccurs="1">
			<xsd:attribute name="locale" 
			type="xsd:string"/>
		</xsd:element>
		<xsd:element name="price" type="xsd:decimal" 
		minOccurs="1">
			<xsd:attribute name="locale" 
			type="xsd:string"/>
			<xsd:attribute name="unit" 
			type="xsd:string"/>
		</xsd:element>
		<xsd:attribute name="sku" type="xsd:decimal"/>
		<xsd:attribute name="name" type="xsd:string"/>
	</xsd:complexType>
</xsd:schema>

Poza standardowymi możliwościami walidacji akie posiada DTD XML Schema poszerza je o ograniczenia co do kolejności występowania elementów w dokumencie, limitów na minimalna i maksymalna ilość wystąpień, wartości domyślnych itp.

XSL

Technologia XSL pozwala w przypadku, kiedy mamy odseparowaną w aplikacji warstwę danych od prezentacji, w łatwy sposób wygenerować na podstawie dokumentu z danymi oraz szablonu prezentacji sformatowane dane wyjściowe. Takie rozwiązanie pozwala w łatwy sposób na podstawie identycznych danych przygotowywać różnie przygotowane prezentacje. Najprostszym przykładem dla aplikacji internetowych będzie sytuacja, kiedy mając dane do zaprezentowania będziemy chcieli inaczej te dane zaprezentować klientowi. W przypadku klienta, który odwołuje sie do aplikacji ze zwykłej przeglądarki WWW wygenerujemy dla niego kod HTML, zawierający dane a poza nimi obrazki i inne elementy graficzne. W przypadku klienta, który będzie się odwoływał do tej samej aplikacji za pomocą prostego aparatu komórkowego, zostanie dla niego wygenerowany dokument WML, który ze względu na zdecydowanie mniejszą ilość danych jakie takie aparaty mogą przetworzyć, będzie zawierał w większości tylko same dane z małą tylko ilością znaczników WML. Standard ten pozwala swobodnie generować wyjściowe dane. Poza zwykłym umieszczeniem danych XML w szablonie XSL posiada on instrukcje warunkowe, pętle, zmienne globalne i lokalne, które w szablonie można używać i wiele innych rozwiązań. Przykładowy dokument XSL może mieć postać:


<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
version="1.0">
<xsl:template match="/">
<html>
<head><title>My Products</title></head>
	<body>
	<h1>Products Currently For Sale in the U.S.</h1>
	<xsl:for-each select="//product">
		<xsl:value-of select="@name"/> : $
		<xsl:value-of select="./price[@unit='USD']"/> USD
	</xsl:for-each>
	</body>
</html>
</xsl:template>
</xsl:stylesheet>

Przykładowy kod JAVA, którego zadaniem jest transformacja pliku XML i XSL może mieć postać:


	StringReader xmlStream= new StringReader(xml);
	StringReader xslStream= new StringReader(xsl);
		
	Source xmlSource= new StreamSource(xmlStream);
	Source xslSource= new StreamSource(xslStream);
		
	TransformerFactory tf = TransformerFactory.newInstance();
	Transformer transformer =tf.newTransformer(xslSource);
	transformer.transform(xmlSource, new StreamResult(out));

XML JAVA API

W przypadku API JAVA rozróżniamy dwa rodzaje parsowania dokumentu XML, SAX oraz DOM. Jako parsowanie rozumie się rozbicie dokumentu na jego składowe i opeacje na danych w nim się znajdujących. Wymienione sposoby zaimplementowane w parserach XML różnią się pod wieloma względami i przystosowane są do innego rodzaju dokumentów XML. Odróżnia ich sposób w jaki operują na dokumencie i co za tym idzie różnią sie wydajnością.

SAX

W przypadku parsera SAX (Simple API for XML) mamy do czynienia z podejściem zdarzeniowym w pasowaniu dokumentów XML. Podczas parsowania dokumentu występują zdarzenia z nim związane takie jak rozpoczęcie parsowania, pojawienie sie otwierającego tagu znacznika, pojawienie się wartości danego znacznika, itp. Zadaniem programisty jest wyłącznie zaimplementowanie we własnej klasie metod odpowiedniego interfejsu, które wywoływane są w przypadku pojawienia sie odpowiednich zdarzeń XML. Ze względu na swoje podejście zdarzeniowe parser ten nie zużywa dużej ilości pamięci, ponieważ nie przechowuje w pamięci całego dokumentu XML, tylko jego mały fragment związany z obsługą danego zdarzenia. Parser ten działa w oparciu o model sekwencyjny, gdzie nie ma możliwości swobodnego dostępu do dokumentu XML i trzeba brać to od uwagę, ponieważ w pewnych okolicznościach jest to zaletą, a w pewnych wadą. Zaleta jest taka, że ze względy właśnie na orientację zdarzeniową i małą konsumpcję pamięci będzie to dość wydajny parser. Jego wadą będzie z tego samego powodu właśnie brak możliwości swobodnego dostępu do dokumentu XML. Jeśli parser w danym momencie będzie w trakcie wywołania metody implementującej rozpoczęcie danego znaczniki, nie mamy w tej metodzie dostępu do informacji o znaczniku poprzednim, następnym, jego rodzicu, czy też znacznikach w nim zawartych. Metoda SAX często również jest stosowana, kiedy struktura dokumentu XML nie jest ściśle określona.

Aby przeparsować dokument XML można posłużyć się poniższym przykładem:

SAXParser parser = new SAXParser();
parser.setContentHandler(xmlHandler);
InputSource iSrc= new InputSource(new java.io.StringReader(xml_data));
parser.parse(iSrc);

W przykładzie tym mając dane xml w zmiennej xml_data dokonano parsowania tych danych za pomocą własnej klasy xmlHandler. Klasa ta powinna implementować interfejs org.xml.sax.ContentHandler.

Poniżej znajduje się lista ciekawszych metod jakie taka klasa powinna zaimplementować.

public void characters (char ch[], int start, int length)
  • char ch[]zawartość tagu,
  • int startpoczątek w tablicy,
  • int lengthilość znaków

Metoda wywoływana kiedy parser napotka wartość danego tagu, np. dla dokumentu <data>My data</data> metoda będzie wywołana jeśli parser napotka wartość "My data".

public void startElement ( String namespaceURI, String localName, String qName, Attributes atts)
  • String namespaceURIprzestrzeń nazw danego znacznika,
  • String localNamenazwa lokalna lub pusty łańcuch jeśli nie było przestrzeni nazw,
  • String qNamenazwa znacznika,
  • Attributes attstablica atrybutów

Metoda wywoływana, kiedy napotkano początek znacznika XML, np. <data name="person"> .

public void endElement (String namespaceURI, String localName, String qName)
  • String namespaceURIprzestrzeń nazw danego znacznika,
  • String localNamenazwa lokalna lub pusty łańcuch jeśli nie było przestrzeni nazw,
  • String qNamenazwa znacznika

Metoda wywoływana, kiedy napotkano koniec znacznika XML, np. &lt;/data&gt;.

public void startDocument()

Metoda wywoływana, kiedy napotkano początek dokumentu XML.

public void endDocument()

Metoda wywoływana, kiedy napotkano koniec dokumentu XML. Przykładowy kod parsujący dokument za pomocą SAX może mieć postać:

import java.util.*;
import org.apache.xerces.parsers.SAXParser;
import org.xml.sax.*;

public class XMLProcessor implements ContentHandler 
{
	public static void main(String args[]) throws Exception {
	XMLProcessor processor = new XMLProcessor();
	processor.execute("<a><b>sssssssssssss</b><c>ABC</c></a>");
	}
	
	public void execute(String indata) throws Exception
	{
	SAXParser parser = new SAXParser();
	parser.setContentHandler(this);
	InputSource iSrc= 
	new InputSource(new java.io.StringReader(indata));
	parser.parse(iSrc);
	}

	public void characters(char[] arg1, int arg2, int arg3) 
	throws SAXException {	
	System.out.println("" + new String(arg1, arg2, arg3) );
	}

	public void startPrefixMapping(String tmp1,String tmp2)
	throws SAXException {
	}

	public void endPrefixMapping(String tmp1,String tmp2)
	throws SAXException {
	}
	
	public void processingInstruction(String tmp1,String tmp2)
	throws SAXException {
	}

	public void endDocument() 
	throws SAXException {
	}

	public void endElement(String arg1,String arg2,String arg3) 
	throws SAXException {
	}

	public void ignorableWhitespace(char[] arg1, 
	int arg2, int arg3)
	throws SAXException {
	}
		
	public void startDocument() 
	throws SAXException {
	}
	
	public void startElement(
	String namespaceURI, String localName, String qName, 
	Attributes atts)
	throws SAXException {
	System.out.println("<"+qName+">");
	}

	public void endPrefixMapping(String prefix) 
	throws SAXException {
	}

	public void skippedEntity(String name) 
	throws SAXException {
	}

	public void setDocumentLocator(Locator locator) {
	}
}

DOM

Metoda DOM (Documen Object Model) parsowania dokumentów XML stanowi zupełnie inne podejście niż to zaprezentowane w przypadku metody SAX. Parser dom wczytuje do pamięci cały dokument, a następnie tworzy na jego podstawie drzewo obiektów reprezentujących ten dokument. Obiekt reprezentujący takie drzewo jest przez parser zwracany, a programista może swobodnie przechodzić pomiędzy elementami dokumentu XML, odczytywać jego zawartość, dokonywać operacji na drzewie poprzez dodawanie, modyfikację i usuwanie tagów XML, ich wartości, czy też atrybutów. Daje to duża swobodę, jednak ma to swoje często bardzo istotne wady. Metoda ta w porównaniu do metody SAX jest zdecydowanie mniej wydajna i bardziej pamięciożerna właśnie ze względu na konieczność przechowywania w pamięci całego dokumentu oraz generowania drzewa XML. Z tego powodu metoda ta nadaje się raczej do obsługi małych rozmiarów dokumentówXML i kiedy niezbyt zależy nam tym aby dokument był bardzo szybko sparsowany.

Przykład aplikacji parsującej dokument za pomocą metody DOM może mieć postać:

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import java.io.File;
	
public class JAXPandDOM {
	
	public static void main(String[] args) 
	{ 
		try { 
		DocumentBuilderFactory factory = 
		DocumentBuilderFactory.newInstance(); 
		DocumentBuilder builder = factory.newDocumentBuilder();
		File ourExample = new File("product-catalog.xml"); 
		Document document = builder.parse( ourExample );
		} 
		catch (Exception e) 
		{ 
		System.out.println(e.getMessage()); 
		} 
	} 
}	

W powyższym przykładzie obiekt document będzie zawierał reprezentację całego dokumentu XML w postaci drzewa.


powrót
Zachęcam do przedstawienia swoich uwag i opinii w polu komentarzy.

Komentarze

Dodaj Komentarz