Einbau eigener Serialisierer
XMLEncoder arbeitet auch mit neuen, selbst definierten Serialisierern. Zum Beispiel sind in der weiter vorne definierten Klasse PlayersBean.java vier Methoden erforderlich, um die indexierte Property players getreu Konvention umzusetzen. Diese Bean-Klasse weist Eigenschaften auf, die zum Teil etwas störend sind: - Die intern verwendete Liste muss in Settern und Gettern in Arrays konvertiert werden.
- Der Array-Setter übernimmt eine Referenz in das Objekt, die der Aufrufer bereitstellt. Eine solche „externe” Referenz aus fremder Quelle widerspricht der Datenkapselung, weil der Aufrufer im Nachhinein das betreffende Objekt nach Belieben manipulieren kann.
- Die void-Ergebnistypen verhindern ein „Fluent Interface”.
- Der volle Satz aller vier Methoden ist etwas ausladend und wird vielleicht nicht gebraucht.
Wegfall der Beans-Konventionen
Die folgende Klasse Players räumt mit diesen Problemen auf und bietet eine kompaktere und sicherere Schnittstelle. Sie folgt allerdings überhaupt nicht mehr den eingangs vorgestellten Beans-Konventionen. import java.util.*;
public class Players {
private final List<Player> players = new ArrayList<>();
public List<Player> getPlayers() {
return Collections.unmodifiableList(players);
}
public Players addPlayer(Player player) {
players.add(player);
return this;
}
}
Players.java: Klasse mit einer Liste von Spielern.
Mit einem passenden Serialisierer kann auch diese Klasse verwendet werden. Der Serialisierer muss allerdings dafür sorgen, dass ein Players-Objekt bei der Deserialisierung durch addPlayer-Aufrufe mit deserialisierten Player-Objekten gefüllt wird. Dazu wird von DefaultPersistenceDelegate eine neue Klasse PlayersPersistenceDelegate abgeleitet, die die Methode initialize redefiniert. initialize legt fest, wie ein Objekt deserialisiert wird. Die Implementierung in der Basisklasse DefaultPersistenceDelegate sucht nach Methoden gemäß Beans-Konventionen, findet jetzt aber nichts mehr. Der Aufruf super.initialize hat also nur noch einen Default-Konstruktoraufruf zur Folge. Anschließend wird in einer Schleife für jeden Player ein Statement-Objekt erzeugt, das einen Aufruf der Methode addPlayer repräsentiert. import java.beans.*;
public class PlayersPersistenceDelegate extends DefaultPersistenceDelegate {
protected void initialize(Class type, Object old, Object noo, Encoder encoder) {
super.initialize(type, old, noo, encoder);
Players players = (Players)old;
for(Player player: players.getPlayers()) {
Statement statement = new Statement(old, "addPlayer", new Object[] {player});
encoder.writeStatement(statement);
}
}
}
PlayersPersistenceDelegate.java: Serialisierer für eine Klasse mit einer Liste von Spielern.
Registrieren des neuen Serialisierers
Bei der Serialisierung wird ein PlayersPersistenceDelegate als Serialisierer für die Klasse Players registriert und dann automatisch vom XMLEncoder benutzt. import java.beans.*;
import java.io.*;
public class PlayersIO {
public static void main(String... args) throws IOException {
Players players;
if(args.length == 1)
try(OutputStream output = new FileOutputStream(args[0]);
XMLEncoder encoder = new XMLEncoder(output)) {
encoder.setPersistenceDelegate(Player.class,
new DefaultPersistenceDelegate(new String[] {
"name"
}));
Player max = new Player("Max");
max.setScore(5);
Player moritz = new Player("Moritz");
moritz.setScore(3);
players = new Players().addPlayer(max).addPlayer(moritz);
encoder.writeObject(players);
}
else
try(InputStream in = new FileInputStream(args[0]);
XMLDecoder decoder = new XMLDecoder(in)) {
players = (Players)decoder.readObject();
}
System.out.println(players);
}
}
PlayersIO.java: Ersatz des Default-Serialisierers durch einen neuen Serialisierer.
Wieder ist keine Änderung an der Deserialisierung nötig.