Beruflich Dokumente
Kultur Dokumente
package com.thoughtworks.xstream.examples; public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
So let's create a person and convert it to XML...
package com.thoughtworks.xstream.examples; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; public class PersonTest { public static void main(String[] args) { Person person = new Person(); person.setName("Guilherme"); XStream xStream = new XStream(new DomDriver()); System.out.println(xStream.toXML(person)); } }
This results in a really ugly XML code which contains the full class name (including package)...
Creating a PersonConverter
Let's create a simple converter capable of: 1. 2. 3. telling its capable of converting Person's translating a Person instance in XML translate XML into a new Person
We begin creating the PersonConverter class and implementing the Converter interface:
package com.thoughtworks.xstream.examples; import import import import import com.thoughtworks.xstream.converters.Converter; com.thoughtworks.xstream.converters.MarshallingContext; com.thoughtworks.xstream.converters.UnmarshallingContext; com.thoughtworks.xstream.io.HierarchicalStreamReader; com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class PersonConverter implements Converter { public boolean canConvert(Class clazz) { return false; } public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { return null; } }
Now we tell whoever calls us that we can handle only Person's (and nothing else, including those classes which extends Person).
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { Person person = (Person) value; writer.startNode("fullname"); writer.setValue(person.getName()); writer.endNode(); }
We could have called start/end node as many times as we would like (but remember to close everything you open)... and conversion usually takes place when calling the setValue method. And now let's go to the unmarshal. We use the moveDown and moveUp methods to move in the tree hierarchy, so we can simply moveDown, read the value and moveUp.
package com.thoughtworks.xstream.examples; import import import import import com.thoughtworks.xstream.converters.Converter; com.thoughtworks.xstream.converters.MarshallingContext; com.thoughtworks.xstream.converters.UnmarshallingContext; com.thoughtworks.xstream.io.HierarchicalStreamReader; com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class PersonConverter implements Converter { public boolean canConvert(Class clazz) { return clazz.equals(Person.class); } public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { Person person = (Person) value; writer.startNode("fullname"); writer.setValue(person.getName()); writer.endNode(); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Person person = new Person(); reader.moveDown(); person.setName(reader.getValue()); reader.moveUp(); return person;
} }
Now let's register our converter and see how our application main method looks like:
package com.thoughtworks.xstream.examples; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; public class PersonTest { public static void main(String[] args) { Person person = new Person(); person.setName("Guilherme"); XStream xStream = new XStream(new DomDriver()); xStream.registerConverter(new PersonConverter()); xStream.alias("person", Person.class); System.out.println(xStream.toXML(person));
} }
Did you notice how we registered our converter? It's a simple call to registerConverter:
xStream.registerConverter(new PersonConverter());
The final result is:
package com.thoughtworks.xstream.examples; public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return getName(); }
package com.thoughtworks.xstream.examples; import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; public class PersonConverter extends AbstractSingleValueConverter { public boolean canConvert(Class clazz) { return clazz.equals(Person.class); } public Object fromString(String str) { Person person = new Person(); person.setName(string); return person; } }
But even nicer, our XML is also simplified (using the alias for the Person class). Since the String representation is complete, a nested element is not necessary anymore:
<person>Guilherme</person>
Date Converter
Now that we know how the Converter interface works, let's create a simple calendar converter which uses the locale to convert the information. Our converter will receive the Locale in its constructor and we will keep a reference to it in a member variable:
package com.thoughtworks.xstream.examples; import java.util.Locale; import import import import import com.thoughtworks.xstream.converters.Converter; com.thoughtworks.xstream.converters.MarshallingContext; com.thoughtworks.xstream.converters.UnmarshallingContext; com.thoughtworks.xstream.io.HierarchicalStreamReader; com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class DateConverter implements Converter { private Locale locale; public DateConverter(Locale locale) { super(); this.locale = locale; } public boolean canConvert(Class clazz) { return false; } public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { Calendar calendar = (Calendar) value; // grabs the date Date date = calendar.getTime(); // grabs the formatter DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL, this.locale); // formats and sets the value writer.setValue(formatter.format(date)); }
And the other way around... in order to unmarshall, we create a GregorianCalendar, retrieves the localized DateFormat instance, parses the string into a Date and puts this date in the original GregorianCalendar:
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { // creates the calendar GregorianCalendar calendar = new GregorianCalendar(); // grabs the converter DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL, this.locale); // parses the string and sets the time try { calendar.setTime(formatter.parse(reader.getValue())); } catch (ParseException e) { throw new ConversionException(e.getMessage(), e); } // returns the new object return calendar;
}
Note 1: remember that some DateFormat implementations are not thread-safe, therefore don't put your formatter as a member of your converter. Note 2: this implementation will convert other types of Calendar's to GregorianCalendar after save/load. If this is not what you want, change your canConvert method to return true only if class equals GregorianCalendar. So we get the following converter:
package com.thoughtworks.xstream.examples; import import import import import import import import import import import import java.text.DateFormat; java.text.ParseException; java.util.Calendar; java.util.Date; java.util.GregorianCalendar; java.util.Locale; com.thoughtworks.xstream.converters.ConversionException; com.thoughtworks.xstream.converters.Converter; com.thoughtworks.xstream.converters.MarshallingContext; com.thoughtworks.xstream.converters.UnmarshallingContext; com.thoughtworks.xstream.io.HierarchicalStreamReader; com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class DateConverter implements Converter { private Locale locale; public DateConverter(Locale locale) { super(); this.locale = locale; } public boolean canConvert(Class clazz) { return Calendar.class.isAssignableFrom(clazz); } public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { Calendar calendar = (Calendar) value; Date date = calendar.getTime(); DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL, this.locale); writer.setValue(formatter.format(date)); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { GregorianCalendar calendar = new GregorianCalendar(); DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL, this.locale); try { calendar.setTime(formatter.parse(reader.getValue())); } catch (ParseException e) {
package com.thoughtworks.xstream.examples; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Locale; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; public class DateTest { public static void main(String[] args) { // grabs the current date from the virtual machine Calendar calendar = new GregorianCalendar(); // creates the xstream XStream xStream = new XStream(new DomDriver()); // brazilian portuguese locale xStream.registerConverter(new DateConverter(new Locale("pt", "br"))); // prints the result System.out.println(xStream.toXML(calendar)); } }
The result? Well... it depends, but it will be something like:
// loads the calendar from the string Calendar loaded = (Calendar) xStream .fromXML("<gregorian-calendar>Sexta-feira, 10 de Fevereiro de 2006</gregorian-calendar>");
And print it using the system locale, short date format:
System.out.println(DateFormat.getDateInstance(DateFormat.SHORT).format( loaded.getTime()));
The result might be something like (if your system locale is American English):
2/10/06
Complex Converter
Setting up another example
We already defined some classes, so let them glue together:
package com.thoughtworks.xstream.examples; public class Birthday { private Person person; private Calendar date; public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } public Calendar getDate() { return date; } public void setDate(Calendar date) { this.date = date; } }
While XStream is capable of converting this class without any problem, we write our own custom converter just for demonstration. This time we want to reuse our already written converters for the Person and the Calendar. The canConvert method is plain simple. We convert no derived classes this time, since they might have additional fields. But we reuse the converters registered in XStream for our member fields and handle null values:
package com.thoughtworks.xstream.examples; import java.util.Calendar; import import import import import com.thoughtworks.xstream.converters.Converter; com.thoughtworks.xstream.converters.MarshallingContext; com.thoughtworks.xstream.converters.UnmarshallingContext; com.thoughtworks.xstream.io.HierarchicalStreamReader; com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class BirthdayConverter implements Converter { public boolean canConvert(Class clazz) { return Birthday.class == clazz; }
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { Birthday birthday = (Birthday)value; if (birthday.getPerson() != null) { writer.startNode("person"); context.convertAnother(birthday.getPerson()); writer.endNode(); } if (birthday.getDate() != null) { writer.startNode("birth"); context.convertAnother(birthday.getDate()); writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Birthday birthday = new Birthday(); while (reader.hasMoreChildren()) { reader.moveDown(); if ("person".equals(reader.getNodeName())) { Person person = (Person)context.convertAnother(birthday, Person.class); birthday.setPerson(person); } else if ("birth".equals(reader.getNodeName())) { Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class); birthday.setDate(date); } reader.moveUp(); } return birthday; } }
If the implementation of Birthday ensures, that none of its fields could hold a null value, then we could drop the null condition in the marshal method and in unmarshal we could omit the loop as well as the comparison of the tag names:
package com.thoughtworks.xstream.examples; import java.util.Calendar; import import import import import com.thoughtworks.xstream.converters.Converter; com.thoughtworks.xstream.converters.MarshallingContext; com.thoughtworks.xstream.converters.UnmarshallingContext; com.thoughtworks.xstream.io.HierarchicalStreamReader; com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class BirthdayConverter implements Converter { public boolean canConvert(Class clazz) { return Birthday.class == clazz; }
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { Birthday birthday = (Birthday)value; writer.startNode("person"); context.convertAnother(birthday.getPerson()); writer.endNode(); writer.startNode("birth"); context.convertAnother(birthday.getDate()); writer.endNode(); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Birthday birthday = new Birthday(); reader.moveDown(); Person person = (Person)context.convertAnother(birthday, Person.class); birthday.setPerson(person); reader.moveUp(); reader.moveDown(); Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class); birthday.setDate(date); reader.moveUp(); return birthday; } }