Converter Tutorial
2019獨角獸企業重金招聘Python工程師標準>>>
Simple Converter
Setting up a simple example
This is the most basic converter... let's start with a simple Person:
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)...
<com.thoughtworks.xstream.examples.Person><name>Guilherme</name> </com.thoughtworks.xstream.examples.Person>So we make use of an 'alias' to change this full class name to something more 'human', for example 'person'.
XStream?xStream?=?new?XStream(new?DomDriver()); xStream.alias("person",?Person.class); System.out.println(xStream.toXML(person));And the outcome is much easier to read (and smaller):
<person><name>Guilherme</name> </person>Now that we have configured a simple class to play with, let's see what XStream converters can do for us...
Creating a PersonConverter
Let's create a simple converter capable of:
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?com.thoughtworks.xstream.converters.Converter; import?com.thoughtworks.xstream.converters.MarshallingContext; import?com.thoughtworks.xstream.converters.UnmarshallingContext; import?com.thoughtworks.xstream.io.HierarchicalStreamReader; import?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?boolean?canConvert(Class?clazz)?{return?clazz.equals(Person.class); }The second step is usually quite clean, unless you are dealing with generic converters.
The marshal method is responsible for translating an object to XML. It receives three arguments:
the object we are trying to convert
the writer were we should output the data
the current marshalling context
We start casting the object to person:
Person?person?=?(Person)?value;Now we can output the data... let's start creating a node called?fullname?and adding the person's name to it:
writer.startNode("fullname"); writer.setValue(person.getName()); writer.endNode();Quite simple huh?
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.
Person?person?=?new?Person(); reader.moveDown(); person.setName(reader.getValue()); reader.moveUp();Which gives us the following converter:
package?com.thoughtworks.xstream.examples;import?com.thoughtworks.xstream.converters.Converter; import?com.thoughtworks.xstream.converters.MarshallingContext; import?com.thoughtworks.xstream.converters.UnmarshallingContext; import?com.thoughtworks.xstream.io.HierarchicalStreamReader; import?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:
<person><fullname>Guilherme</fullname> </person>So you might say... that only changed my tree, I want to convert data!
Try using an attribute called?fullname?in the?person?tag instead of creating a new child node.
An alternative for types with String representation
Let's enhance the Person with a String representation, that contains all necessary text to recreate the instance:
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();} }In this case we can simplify our Converter to
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>Note, that in implementation of a SingleValueConverter is required for attributes, since these objects have to be represented by a single string only.
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?com.thoughtworks.xstream.converters.Converter; import?com.thoughtworks.xstream.converters.MarshallingContext; import?com.thoughtworks.xstream.converters.UnmarshallingContext; import?com.thoughtworks.xstream.io.HierarchicalStreamReader; import?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?Object?unmarshal(HierarchicalStreamReader?reader,UnmarshallingContext?context)?{return?null;}}Now let's convert anything which extends?Calendar: means if instances of class?clazz?can be assigned to the?Calendar?class, they extends the abstract class?Calendar:
public?boolean?canConvert(Class?clazz)?{return?Calendar.class.isAssignableFrom(clazz); }Let's go for converting a?Calendar?in a localized string... we first cast the object to?Calendar, extract its?Date?and then use a?DateFormat?factory method to get a date converter to our localized string.
public?void?marshal(Object?value,?HierarchicalStreamWriter?writer,MarshallingContext?context)?{Calendar?calendar?=?(Calendar)?value;//?grabs?the?dateDate?date?=?calendar.getTime();//?grabs?the?formatterDateFormat?formatter?=?DateFormat.getDateInstance(DateFormat.FULL,this.locale);//?formats?and?sets?the?valuewriter.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 originalGregorianCalendar:
public?Object?unmarshal(HierarchicalStreamReader?reader,UnmarshallingContext?context)?{//?creates?the?calendarGregorianCalendar?calendar?=?new?GregorianCalendar();//?grabs?the?converterDateFormat?formatter?=?DateFormat.getDateInstance(DateFormat.FULL,this.locale);//?parses?the?string?and?sets?the?timetry?{calendar.setTime(formatter.parse(reader.getValue()));}?catch?(ParseException?e)?{throw?new?ConversionException(e.getMessage(),?e);}//?returns?the?new?objectreturn?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?equalsGregorianCalendar.
So we get the following converter:
package?com.thoughtworks.xstream.examples;import?java.text.DateFormat; import?java.text.ParseException; import?java.util.Calendar; import?java.util.Date; import?java.util.GregorianCalendar; import?java.util.Locale;import?com.thoughtworks.xstream.converters.ConversionException; import?com.thoughtworks.xstream.converters.Converter; import?com.thoughtworks.xstream.converters.MarshallingContext; import?com.thoughtworks.xstream.converters.UnmarshallingContext; import?com.thoughtworks.xstream.io.HierarchicalStreamReader; import?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)?{throw?new?ConversionException(e.getMessage(),?e);}return?calendar;}}And let's try it out. We create a?DateTest?class with a?main?method:
creates a calendar (current date)
creates the XStream object
registers the converter with a Brazilian Portuguese locale
translates the object in XML
Well, we already know how to do all those steps... so let's go:
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?machineCalendar?calendar?=?new?GregorianCalendar();//?creates?the?xstreamXStream?xStream?=?new?XStream(new?DomDriver());//?brazilian?portuguese?localexStream.registerConverter(new?DateConverter(new?Locale("pt",?"br")));//?prints?the?resultSystem.out.println(xStream.toXML(calendar));}}The result? Well... it depends, but it will be something like:
<gregorian-calendar>Sexta-feira,?10?de?Fevereiro?de?2006</gregorian-calendar>Note: we did not put any alias as?gregorian-calendar?is the default alias for?GregorianCalendar.
And now let's try to unmarshal the result shown above:
//?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:
//?prints?using?the?system?defined?locale 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/06Complex 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;private?char?gender;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;}public?char?getGender()?{return?gender;}public?void?setGenderMale()?{this.gender?=?'m';}public?void?setGenderFemale()?{this.gender?=?'f';}}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 and add an own attribute for the gender. 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?com.thoughtworks.xstream.converters.ConversionException; import?com.thoughtworks.xstream.converters.Converter; import?com.thoughtworks.xstream.converters.MarshallingContext; import?com.thoughtworks.xstream.converters.UnmarshallingContext; import?com.thoughtworks.xstream.io.HierarchicalStreamReader; import?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.getGender()?!=?'\0')?{writer.addAttribute("gender",?Character.toString(birthday.getGender()));}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();String?gender?=?reader.getAttribute("gender");if?(gender?!=?null)?{if?(gender.length()?>?0)?{??????????????if?(gender.char(0)?==?'f')?{birthday.setGenderFemale();}?else?if?(gender.char(0)?==?'m')?{birthday.setFemale();}?else?{throw?new?ConversionException("Invalid?gender?value:?"?+?gender);}}?else?{throw?new?ConversionException("Empty?string?is?invalid?gender?value");}}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;}}The unmarshal method ensures the valid value for the gender by throwing a ConversionException for invalid entries.
Note, that attributes will always have to be written and read first. You work on a stream and accessing the value of a tag or its members will close the surrounding tag (that is still active when the method is called).
If the implementation of?Birthday?ensures, that none of its fields could hold a?null?value and gender contains a valid 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?com.thoughtworks.xstream.converters.Converter; import?com.thoughtworks.xstream.converters.MarshallingContext; import?com.thoughtworks.xstream.converters.UnmarshallingContext; import?com.thoughtworks.xstream.io.HierarchicalStreamReader; import?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.addAttribute("gender",?Character.toString(birthday.getGender()));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();if?(reader.getAttribute("gender").charAt(0)?==?'m')?{birthday.setGenderMale();}?else?{birthday.setGenderFemale();}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;}}轉載于:https://my.oschina.net/heroShane/blog/199202
總結
以上是生活随笔為你收集整理的Converter Tutorial的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 批量刷新远程物化视图的方法(备用)
- 下一篇: js中变量原理