Sieben XStream Tipps
1. XML-Prolog schreiben
Das kann XStream nicht, aber beim Lesen wird der XML-Prolog überlesen. Man muss den Prolog also von Hand in den Strom schreiben:
Writer w = new OutputStreamWriter(
new BufferedOutputStream(
new FileOutputStream( file ) ), "utf-8" );
w.write( "<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>\\n" );
xstream.toXML( value, w );
w.close();
2. Statt voll qualifizierte Klassenamen kann ein anderer Name mit alias() auf dem XStream-Objekt gesetzt werden.
xstream.alias( "Course", Course.class );
xstream.alias( "Contact", Contact.class );
3. Statt geschachtelte Elemente sollen gewisse Dinge in Attributen stehen:
xstream.useAttributeFor( TrainerCourse.class, "price" );
xstream.useAttributeFor( TrainerCourse.class, "languages" );
4. Statt Elemente von Sammlungen in ein eigenes Container-Element zu setzen, kann das auch ohne Überelement gesetzt werden:
xstream.addImplicitCollection( Trainer.class, "trainerCourses" );
5. Gewisse Dinge sollen anders geschrieben werden. Konverter sind angebracht:
xstream.registerConverter( new TrainerCourseConverter() );
xstream.registerConverter( new YYYYMMDDConverter() );
Konverter können ganz eigenständig entwickelt werden (1. Bsp.), oder eine Basisklasse (2. Bsp.) vereinfacht das:
class TrainerCourseConverter implements Converter
{
@Override
public void marshal( Object value, HierarchicalStreamWriter writer, MarshallingContext arg2 )
{
TrainerCourse tc = (TrainerCourse) value;
writer.addAttribute( "courseCode", c.getCourseCode() );
}
@Override
public Object unmarshal( HierarchicalStreamReader arg0, UnmarshallingContext arg1 )
{
…
}
@SuppressWarnings( "unchecked" )
@Override
public boolean canConvert( Class clazz )
{
return clazz.equals( TrainerCourse.class );
}
}
Zweiter einfacher Konverter:
class YYYYMMDDConverter extends AbstractSingleValueConverter
{
@SuppressWarnings( "unchecked" )
public boolean canConvert( Class clazz )
{
return clazz.equals( Date.class );
}
@Override
public Object fromString( String str )
{
return StringUtils2.isEmpty( str ) ? null : DateUtils.parseDDMMYYYY( str );
}
@Override
public String toString( Object obj )
{
return obj == null ? "" : DateUtils.formatDDMMYY( (Date) obj );
}
}
6. Elemente mit bekannten Namen sollen in einer CDATA-Umgebung geschrieben werden:
xstream = new XStream( new XppDriver() {
public HierarchicalStreamWriter createWriter( Writer out ) {
return new PrettyPrintWriter( out ) {
boolean isCDATA = false;
@SuppressWarnings( "unchecked" ) @Override public void startNode( String name, Class clazz ) {
super.startNode( name, clazz );
isCDATA = name.equals( "contents" ) || name.equals( "description" );
}
@Override protected void writeText( QuickWriter writer, String text ) {
if ( isCDATA ) { writer.write( "<![CDATA[\\n" ); writer.write( text ); writer.write( "\\n]]>" ); }
else super.writeText( writer, text );
}
};
}
} );
7. XStream soll XML-Dateien selbst laden und speichern ()
Verwaltet man viele kleine XML-Dokumenten, etwa Kontakte, kann XStream diese XML-Kontakt-Dateien automatisch anlegen, lesen und aktualisieren. Der Tick ist, dass man alle Daten in eine spezielle Map, Set oder List setzt. Die XStream-Implementierungen der add(), put(), get(), … Methoden sind so realisiert, dass die Daten beim add()/put() in die Datenstruktur und gleichzeitig XML-serialisiert als Datei ins Dateisystem kommen. In welches Verzeichnis sagt die FilePersistenceStrategy.
PersistenceStrategy strategy = new FilePersistenceStrategy(new File("/tmp"));
Map map= new XmlMap( strategy );
Die Dateinamen sind etwas merkwürdig, etwa long@12345676, aber dass ist kein Wunder, denn der Dateiname kodiert den Schlüssel, denn XStream muss ja bei einem get() etwa die XML-Datei wiederfinden.