Archiv für die Kategorie ‘XML’

XStream: Object -> XML -> Object

Donnerstag, 19. November 2009

Mit der Bibliothek XStream lassen sich auf einfache Weise Java Objekte in XML zu serialisieren und wieder zurück.
Das kann z.B. dazu dienen, Datensätze als Textdatei abzuspeichern oder XML für Unit-Tests zu erzeugen.
Vorteile von XStream sind die sehr einfache Anwendung und die gute Performance. Neuere Versionen von XStream bieten zudem die Möglichkeit auch JSON zu erzeugen oder die Integration in andere XML APIs.

Nun ein paar Beispiele zur Verwendung von XStream.

XStream verwendet per default die Pull-Parser Implementierung XPP3. Das entsprechende JAR muss hierzu separat heruntergeladen und im Classpath hinzugefügt werden. (Die XPP3 Implementierung am Besten über eine Suchmaschine im Internet suchen.)

Anmerkung:
Wir verwenden hier Aliase für Klassennamen. Das hat den Vorteil, dass das XML lesbarer wird und vor allem eine Deserialisierung auch nach einem Umbenennen oder Verschieben der Klasse funktioniert.

Objekt nach XML serialisieren

  final XStream xstream = new XStream();
  // set alias for the MyData class 
  xstream.alias("my-data", MyData.class);
  // MyData myData = new MyData(....);
  final String xml = xstream.toXML(myData);

Objekt aus XML deserialisieren

  final XStream xstream = new XStream();
  // set alias for the MyData class 
  xstream.alias("my-data", MyData.class);
  // String xml = 
  MyData myData = (MyData) xstream.fromXML(xml);

Interaktion mit dom4J

Mit XStream lassen sich auch Objekte in ein bestehendes XML Document einfügen. In diesem Beispiel wird ein dom4j Element ermittelt und an die Methode addToElement() übergeben, welche das Objekt myData in das Element serialisiert:

  public void addToElement(final MyData myData, final org.dom4j.Element element) {
    final XStream xstream = new XStream();
    xstream.alias("my-data", MyData.class);
    // create new com.thoughtworks.xstream.io.xml.Dom4JWriter
    final Dom4JWriter writer = new Dom4JWriter(element);
    xstream.marshal(myData, writer);
    writer.close();
  }

Natürlich ist der Weg zurück zum Objekt aus dem XML genauso einfach:

  public MyData getFromElement(final org.dom4j.Element element) {
    final XStream xstream = new XStream();
    xstream.alias("my-data", MyData.class);
    final Dom4JReader reader = new Dom4JReader(element);
    return (MyData) xstream.unmarshal(reader);
  }

Viel Spaß und Erfolg beim Ausprobieren und Anwenden.


Christof Aenderl


XML <=> Objekt mit JAXB

Donnerstag, 12. Februar 2009

Gründe warum man Objekte direkt in XML umwandeln will (oder anders herum) gibt es viele. Mit JAXB steht im Java SDK 1.6 jetzt eine besonders einfache Möglichkeit zur Verfügung.

Für die Umwandlung eines einfachen Objektes reicht bereits eine Annotation an der Klasse sowie der Aufruf des JAXB Marschallers.

Eine einfache Klasse mit der @XmlRootElement annotation:

@XmlRootElement
public class Person {
    private String firstname;
    private String name;
 
    public Person() {
    }
 
    public String getFirstname() {
        return firstname;
    }
 
    public void setFirstname(final String firstname) {
        this.firstname = firstname;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(final String name) {
        this.name = name;
    }
}

Mit dem Marshaller wird aus einem Objekt eine XML Datei.

public class Test {
    public static void main(final String[] args) throws Exception {
        final Person person = new Person();
        person.setFirstname("Max");
        person.setName("Mustermann");
 
        //  JAXBContext erstellen mit
        final JAXBContext jc = JAXBContext.newInstance(Person.class);
 
        // Marshaller erstellen
        final Marshaller marshaller = jc.createMarshaller();
 
        // person objekt nach XML wandeln und in eine Datei schreiben.
        marshaller.marshal(person,new File("person.xml"));
    }
}

Unmarshalling einer XML Datei in ein Objekt:

public class Test {
    public static void main(final String[] args) throws Exception {
        //  JAXBContext erstellen mit
	final JAXBContext jc = JAXBContext.newInstance(Person.class);
 
        // Marshaller erstellen
        final Unmarshaller unmarshaller = jc.createUnmarshaller();
 
        // XML auslesen und person Objekt befuellen
        final Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));
        System.out.println(person.getFirstname());
        System.out.println(person.getName());
    }
}

Um Einfluss auf die Struktur des erzeugten XMLs zu nehmen bietet sich ein Blick in das Paket javax.xml.bind.annotation an. Mit Hilfe dieser Annotations kann die Struktur des erstellten XML beeinflusst werden, bzw. die Klasse kann an ein bestehendes XML angepasst werden.

Wenn große XML Dateien eingelesen werden sollen, so ist dies in den seltensten Fällen mit einer passenden Klasse erledigt. Wenn aber viele kleine Klassen erstellt werden, muss man dem JAXBContext beim erstellen sehr viele Klassen bekannt machen. Einfacher ist es in das Paket neben die Klassen für die XML Umwandlung eine Datei jaxb.index zu legen. In dieser Datei werden alle Klassen aufgeführt werden die dem JAXBContext bekannt gemacht werden sollen. Beim erzeugen des JAXBContext brauchen nun keine Klassennamen mehr übergeben werden.


Felix Breske


Rest Services mit Spring und Restlet

Montag, 10. November 2008

Mit der Version 1.1 (aktuell RC2) bekommt das Restlet Framework eine Integration für Spring. Die Konfiguration erfolgt über Web- und Spring-Context der Anwendung. Hierfür stehen dem Entwickler die Klassen RestletFrameworkServlet und SpringRouter zur Verfügung.

Im Gegensatz zu Restlet 1.0 braucht der “Restlet Router” und die “Restlet Application” nicht mehr von dem Entwickler implementiert werden. Nach der Konfiguration in der web.xml und applicationContext.xml müssen nur noch die Anwendungsspezifischen Resources selbst umgesetzt werden. Das Mapping der Resource-URL auf die Restlets erfolgt dabei über die applicationContext.xml.

RestletFrameworkServlet
Das RestletFrameworkServlet stellt die Verbindung zwischen Web Context und Spring Konfiguration für Restlet da. Es nimmt die HTTP Anfragen entgegen und leitet sie an den konfigurierten Router um.

web.xml

<!-- Restlet -->
<servlet>
	<servlet-name>restlet</servlet-name>
	<servlet-class>com.noelios.restlet.ext.spring.RestletFrameworkServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/applicationContext.xml</param-value>
	</init-param>
	<init-param>
		<param-name>targetRestletBeanName</param-name>
		<param-value>springRouter</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
 
<servlet-mapping>
	<servlet-name>restlet</servlet-name>
	<url-pattern>/*</url-pattern>
</servlet-mapping>

SpringRouter
Der Spring Router verteilt die eingehenden Anfragen an die konfigurierten Rest Resources. Im Spring Context wird dabei ein Mapping von URLs auf Restlet Beans vorgenommen. Also Beispiel wird hier eine Klasse Note verwendet die eine Id und einen Text beinhaltet.

applicationContext.xml

 
<!-- restlet -->
<bean id="springRouter" class="org.restlet.ext.spring.SpringRouter">
	<property name="attachments">
		<map>
			<entry key="/notes/{id}">
				<bean class="org.restlet.ext.spring.SpringFinder">
					<lookup-method name="createResource" bean="noteResource" />
				</bean>
			</entry>
			<entry key="/notes">
				<bean class="org.restlet.ext.spring.SpringFinder">
					<lookup-method name="createResource" bean="notesResource" />
				</bean>
			</entry>
		</map>
	</property>
</bean>
 
<!-- restlet resources -->
<bean id="notesResource" name="notesResource" class="org.example.NotesResource" scope="prototype">
	<property name="noteService" ref="noteService" />
</bean>
 
<bean id="noteResource" name="noteResource" class="org.example.NoteResource" scope="prototype">
	<property name="noteService" ref="noteService" />
</bean>

Jetzt fehlen nur noch die Implementierung der Resource Beans. Hier muss lediglich beachtet werden, dass die Resources einen parameterlosen Konstruktor haben und die Context spezifischen Parameter über die init() Methode übergeben werden.

NoteResource.java

public class NoteResource extends Resource {
	private Note note;
	private NoteDAO noteDAO;
 
	public NoteResource() {
	}
 
	public void setNoteDAO(final NoteDAO noteDAO) {
		this.noteDAO = noteDAO;
	}
 
	@Override
	public void init(final Context context, final Request request, final Response response) {
		super.init(context, request, response);
		final Integer noteId = Integer.parseInt((String) getRequest().getAttributes().get("id"));
		note = noteDAO.findById(noteId);
		if (note != null) {
			getVariants().add(new Variant(MediaType.TEXT_XML));
		}
	}
 
	@Override
	public Representation getRepresentation(final Variant variant) {
		if (MediaType.TEXT_XML.equals(variant.getMediaType())) {
			try {
				final DomRepresentation representation = new DomRepresentation(MediaType.TEXT_XML);
				final Document document = representation.getDocument();
 
				final Element eltItem = document.createElement("note");
				document.appendChild(eltItem);
				final Element idElement = document.createElement("id");
				idElement.appendChild(document.createTextNode(Integer.toString(note.getId())));
				eltItem.appendChild(idElement);
 
				final Element textElement = document.createElement("text");
				textElement.appendChild(document.createTextNode(note.getText()));
				eltItem.appendChild(textElement);
 
				document.normalizeDocument();
 
				return representation;
			} catch (final IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}

NotesResource.java

public class NotesResource extends Resource {
	private List<Note> notes;
	private NoteDAO noteDAO;
 
	public NotesResource() {
	}
 
	public void setNoteDAO(final NoteDAO noteDAO) {
		this.noteDAO = noteDAO;
	}
 
	@Override
	public void init(final Context context, final Request request, final Response response) {
		super.init(context, request, response);
		notes = noteDAO.getAllNotes();
		getVariants().add(new Variant(MediaType.TEXT_XML));
	}
 
	@Override
	public Representation getRepresentation(final Variant variant) {
		if (MediaType.TEXT_XML.equals(variant.getMediaType())) {
			try {
				final DomRepresentation representation = new DomRepresentation(MediaType.TEXT_XML);
				final Document document = representation.getDocument();
				final Element root = document.createElement("notes");
				d.appendChild(root);
				for (final Note note : notes) {
					final Element eltItem = document.createElement("note");
					final Element idElement = document.createElement("id");
					idElement.appendChild(document.createTextNode(Integer.toString(note.getId())));
					eltItem.appendChild(idElement);
 
					final Element textElement = document.createElement("text");
					textElement.appendChild(document.createTextNode(note.getText()));
					eltItem.appendChild(textElement);
 
					root.appendChild(eltItem);
				}
				document.normalizeDocument();
				return representation;
			} catch (final IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}

Wird nun die URL /notes (innerhalb der Webanwendung) aufgerufen wird eine Liste aller Einträge angezeigt; mit /notes/0 kann dann direkt auf einen Eintrag zugegriffen werden.

Für schreibende Zugriffe auf die Resources lohnt sich ein Blick in Dokumentation der Resource Klasse und in das Restlet Tutorial auf restlet.org.


Felix Breske


Castor XML

Mittwoch, 04. Juni 2008

Mit Castor XML lassen sich XML Dokumente in Java Objekte umwandeln und umgekehrt. Es handelt sich also um ein so genanntes Databinding Framework. Hierbei werden die Begriffe “marshal” und “unmarshal” benutzt, “marshal” bedeutet die Verarbeitung eines Objekts hin zu einem Stream und “unmarshal” die Gegenrichtung, die Verarbeitung eines Streams zu einem Objekt.

Hinweis: Castor bietet im allgemeinen auch ein Java/SQL Binding an, in diesem Artikel soll es aber nur um die XML Komponente von Castor gehen.

Java Klassen aus XML Schema generieren

Bevor es losgehen kann mit dem ersten Schritt, nämlich aus einer XML Schema Datei (*.xsd) die zugehörigen Java Sources von Castor generieren zu lassen, werden folgende jars benötigt..

..von http://www.castor.org/download.html direkt :

http://dist.codehaus.org/castor/1.2/castor-1.2.jar

http://dist.codehaus.org/castor/1.2/castor-1.2-xml-schema.jar

http://dist.codehaus.org/castor/1.2/castor-1.2-codegen.jar

..von Apache:

http://www.apache.org/dist/velocity/engine/1.5/

Die jars sollten im Classpath enthalten sein bzw. gleich mit angegeben werden. Mit dem folgendem Aufruf aus dem Verzeichnis, in dem sich die beispiel.xsd Datei befindet lassen sich nun die Java Klassen generieren:

java org.exolab.castor.builder.SourceGeneratorMain -i beispiel.xsd

Für die weitere Arbeit mit Castor sind zusätzlich nötig:

http://apache.4any.org/commons/logging/binaries/commons-logging-1.1.tar.gz

http://xerces.apache.org/xerces2-j/download.cgi

Und so kann dann prinzipiell mit den entstandenen Klassen gearbeitet werden..

public class Beispiel {
    public static void main(String[] args) {
        try {
            FileReader reader = new FileReader(“beispiel.xml);
            Object obj = Unmarshaller.unmarshal(Tier.class, reader);
            Tier tier = (Tier) obj;
            System.out.println(tier.getName());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MarshalException e) {
            e.printStackTrace();
        }catch (ValidationException e) {
            e.printStackTrace();
        }
    }
}

Es wird erstmal ein FileReader anhand der XML-Datei erzeugt. Mit Hilfe des FileReader und der Angabe der zur XML-Datei passenden Java Klasse, erzeugt Castor die Java Objekte und füllt diese mit den Daten aus der XML-Datei. Anschließend wird noch ein TypeCast durchgeführt und die Arbeit mit dem erzeugten Java Objekt kann beginnen.

Java Objekte zu XML

Folgender kleiner Code-Schnipsel bewirkt die Gegenrichtung (marshal).

FileWriter writer = new FileWriter("tier.xml");
Marshaller.marshal(tier, writer);
writer.close();

Es wird die Methode “marshal” aufgerufen und ihr ein File Writer mit der zu erzeugenden XML-Datei übergeben. Zusätzlich wird noch das zu verarbeitende Java Objekt (ein Obj. der Klasse Tier in diesem Fall) für den Methodenaufruf benötigt. Castor erzeugt nun automatisch die XML-Struktur.

Weitere Hinweise

Es geht selbstredend viel mehr mit Castor, wie z.B. das Erzeugen von XML-Dateien anhand einer vorgegebenen Mapping Datei, die im XML Format gehalten ist. In ihr werden, wie der Name schon verrät, die Java Objekte und ihre Attribute zu genau spezifizierten XML-Elementen zugeordnet. Damit kann man die Struktur der generierten XML-Datei selbst festlegen und muß sich nicht auf die automatisch von Castor erzeugte Struktur beschränken/verlassen.

Natürlich kann man auch, soweit eine *.xsd Datei vorhanden ist die Java Klassen von Castor generieren lassen, diese mit Werten befüllen und dann in XML-Dateien wandeln, so erhält man eine dem Schema entsprechende Struktur.

Weiterführende Dokumentation/Informationen auf http://www.castor.org/ selbst.


Sven Seiler