Laboratorium XML
wtorek 11.00—12.30
»Home
»Materiały
  »DTD & co.
  »SAX
  »DOM
  »Zadanie 2
  »XPath
  »XSLT
  »Simple
  »JDOM
  »JS
»Odnośniki
  »Xerces2
»Zadania

Krótka powtórka z Java'y

Plik źródłowy pub/sax/Hello.java:

class Hello {
    public static void main(String args[]) {
        System.out.println("hello");
    }
}

Kompilacja i uruchomienie

$ javac Hello.java
$ java Hello
hello

Krótki przykład użycia SAX

Będziemy używać biblioteki Xerces rozwijanej przez fundację Apache, biblioteka Xerces udostępnia m.in. API SAX (Simple API for XML).

import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.Attributes;
import org.xml.sax.XMLReader;
import org.xml.sax.SAXParseException;


class NaiveXML2SOX extends DefaultHandler
{
    public static void main(String args[]) throws Exception
    {
        XMLReader xr = XMLReaderFactory.createXMLReader();
        xr.setFeature("http://xml.org/sax/features/validation", true);
        NaiveXML2SOX writer = new NaiveXML2SOX();
        xr.setContentHandler(writer);
        xr.setErrorHandler(writer);
        xr.parse(args[0]);
    }

    int indention = 0;

    public void startDocument ()
    {
        indention = 0;
    }

    public void printIndention()
    {
        for (int i = 0; i < indention; i++) {
            System.out.print(' ');
        }
    }

    public void startElement(String uri, String name, String qName,
                             Attributes attrs)
    {
        printIndention();
        System.out.println(qName +">");
        indention += 2;
        for (int i = 0; i < attrs.getLength(); i++) {
            printIndention();
            System.out.println(attrs.getQName(i) +
                               "=\"" + attrs.getValue(i) + "\"");
        }
    }

    public void endElement(String uri, String name, String qName)
    {
        indention -= 2;
    }

    public void warning(SAXParseException e)
    {
        System.err.println("warning: " + e.getMessage());
    }

    public void error(SAXParseException e)
    {
        System.err.println("error: " + e.getMessage());
    }

    public void fatalError(SAXParseException e)
    {
        System.err.println("fatal error: " + e.getMessage());
    }
}

Uruchomienie może wymagać, w zależności od środowiska, zaledwie:

java NaiveXML2SOX window1.xml

lub też

java -classpath /usr/share/java/xerces.jar:. \
     -Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser NaiveXML2SOX window1.xml
Note

W przykładach na stronie SAX Quickstart wykorzystuje się uruchomienie parsera dla pliku filename za pomocą

xr.parse(new InputSource(new FileReader("filename")));

takie wywołanie nie daje jednak parserowi informacji o położeniu dokumentu i jeśli tylko w dokumencie pojawi się odwołanie do typu dokumentu zawierające ścieżkę względną

<!DOCTYPE window SYSTEM "window1.dtd">

to spowoduje wystąpienie śmiertelnego błędu (fatal error: File "window1.dtd" not found.), lekarstwem jest podawanie nazwy pliku jako bezpośredni argument metody parse:

xr.parse("filename");

Walidacja za pomocą XML Schema

Możliwe są dwa rozłączne przypadki:

  1. Zarówno dokument jak i odpowiadająca mu deklaracja typu dokumentu XML Schema nie zawierają deklaracji przestrzeni nazw.

  2. Dokument zawiera deklarację przestrzeni nazw (atrybut xmlns (zwykle) głównego elementu dokumentu) identyczną jak zadeklarowana w deklaracji typu dokumentu XML Schema (atrybut targetNamespace głównego elementu schema).

Zobacz również: XML Schema w Xerces

Dokumenty bez deklaracji przestrzeni nazw

Przykładowy plik XML Schema bez deklaracji deklarowanej przestrzeni nazw ma postać

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="root" type="xsd:string"/>
</xsd:schema>

Dokument zgodny z tym schematem i określający adres URI powyższego schematu ma postać

<!DOCTYPE root [
<!ELEMENT root EMPTY>
<!ATTLIST root
          xmlns:xsi CDATA #IMPLIED
          xsi:noNamespaceSchemaLocation CDATA #IMPLIED>
]>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="nonamespace.xsd"/>

w dokumencie tym zastosowano względny adres URI do pliku namespace.xsd, można również podać adres bezwzględny, np. http://manta.univ.gda.pl/~lpankows/xml-wtorek/namespace.xsd.

Jeśli podany w adresie plik nie może zostać odczytany (np. nie istnieje lub docelowy serwer jest niedostępny) to obiekt zarejestrowany do otrzymywania błędnych zdarzeń zostanie o tym poinformowany metodą error().

Może być również tak, że dokument nie zawiera adresu schematu, np.:

<!DOCTYPE xroot [
<!ELEMENT xroot EMPTY>
]>
<xroot />

lub schemat podany w dokumencie może być inny, niż wymagany przez program, w takim wypadkach możemy w programie wymusić użycie konkretnego schematu:

        XMLReader xr = XMLReaderFactory.createXMLReader();
        xr.setFeature("http://xml.org/sax/features/validation", true);
        xr.setProperty("http://apache.org/xml/properties/schema/" +
                       "external-noNamespaceSchemaLocation",
                       "nonamespace1.xml");

Dokumenty z deklaracją przestrzeni nazw

W schemacie XML Schema nazwę przestrzeni nazw deklarujemy atrybutem targetNamespace:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://example.com/mini">
  <xsd:element name="root" type="xsd:string"/>
</xsd:schema>

W dokumencie możemy podać nazwę przestrzeni nazw za pomocą atrybutu xmlns oraz zadeklarować położenie schematu XML Schema

<root xmlns="http://example.com/mini"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://example.com/mini mini.xsd"/>

W dokumencie możemy pominąć adres URI schematu

<root xmlns="http://example.com/mini"/>
<mynamespace:root xmlns:mynamespace="http://example.com/mini"/>

W takim przypadku musimy (a w poprzednim możemy) wymusić użycie konkretnego schematu:

        XMLReader xr = XMLReaderFactory.createXMLReader();
        xr.setFeature("http://xml.org/sax/features/validation", true);
        xr.setProperty(
            "http://apache.org/xml/properties/schema/external-schemaLocation",
            "http://example.com/mini mini.xsd");

Prosty validator dokumentów XMLowych

import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.XMLReader;
import org.xml.sax.SAXParseException;


class SimpleValidator extends DefaultHandler
{
    public static void main(String args[]) throws Exception
    {
        XMLReader xr = XMLReaderFactory.createXMLReader();
        xr.setFeature("http://xml.org/sax/features/validation", true);
        xr.setFeature("http://apache.org/xml/features/validation/schema", true);
        SimpleValidator validator = new SimpleValidator();
        xr.setErrorHandler(validator);
        for (int i = 0; i < args.length; i++) {
            if (args[i].matches("[^ ]*\\.xsd$")) {
                xr.setProperty("http://apache.org/xml/properties/schema/" +
                               "external-noNamespaceSchemaLocation", args[i]);
            } else if (args[i].matches("http.*\\.xsd$")) {
                xr.setProperty(
                    "http://apache.org/xml/properties/schema/external-schemaLocation",
                    args[i]);
            } else {
                xr.parse(args[i]);
            }
        }
        if (validator.errors > 0) {
            System.exit(1);
        }
    }

    int errors = 0;

    private void message(SAXParseException e, String severity)
    {
        System.err.println(e.getSystemId() + ":" +
                           e.getLineNumber() + ":" +
                           e.getColumnNumber() + ": " +
                           severity + ": " +
                           e.getMessage());
    }

    public void warning(SAXParseException e)
    {
        message(e, "warning");
    }

    public void error(SAXParseException e)
    {
        message(e, "error");
        errors += 1;
    }

    public void fatalError(SAXParseException e)
    {
        message(e, "fatal error");
        errors += 1;
    }
}

Listowanie wszystkich zdarzeń parsera

DocumentTracer.java

$ javac -classpath /usr/share/java/xerces.jar:. sax/DocumentTracer.java
$ java -classpath /usr/share/java/xerces.jar:. sax.DocumentTracer sax/window1.xml

Zadanie

Napisać konsolowy program animacyjny wyświetlający w kółko ramki z pliku animacji sax/anim.xml.gz według zawartego w nim typu dokumentu:

<!ELEMENT ascii-animation (frame)*>
<!ELEMENT frame (line)*>
<!ELEMENT line (char)*>
<!ELEMENT char EMPTY>
<!ATTLIST char
          code CDATA #REQUIRED
          count NMTOKEN "1">

Mini ściąga z Java'y:

  • System.out.print("\u001B[8A") to jest 8 linii do góry (8 UP=)

  • "str".equals(name)

  • Thread.sleep(100)

  • attrs.getValue("name")

  • Integer.parseInt("10")