XStream是一个将对象序列化为xml并解析xml为对象的框架,主页位于http://xstream.codehaus.org。使用非常简单
引入依赖:
com.thoughtworks.xstream xstream 1.4.2
需要的依赖
XmlPull一个xmlpull parser api用来判断具体的xml解析实现(DOM、StAX等)工厂
其他可供选择的Xpp3、DOM4J
1、创建待序列化的对象:
Person.java
public class Person { private Integer id; private String username; private String password; private Address address; ... }
Person中包含一个Address作为Field
public class Address { private String street; private String city; ...... }
2、序列化和反序列化
使用XStream只需要实例化一个XStream对象即可:
XStream xstream = new XStream(); //采用这个构造器默认需要依赖:xstream-[version].jar, xpp3-[version].jar and xmlpull-[version].jar或者采用DOM的方式解析:
XStream xstream = new XStream(new DomDriver()); //此时不需要XPP3
或者基于事件的StAX
XStream xstream = new XStream(new StaxDriver()); //如果采用Java 6,也不需要xpp3.将采用默认的JAXB
//对象序列化为xml xstream.toXML(Object) //xml反序列化为对象 xstream.fromXML(xml)
一个例子:
Person p = new Person(); p.setId(1); p.setUsername("robin"); p.setPassword("123"); p.setAddress(new Address("xxRoad", "chengdu")); xstream.toXML(p);
输出为:
1 robin 123 xxRoad chengdu
3、alias
这里可以看到生成的xml中root element名字为class,如果需要修改就需要用到
xstream.alias("person", Person.class);
这样就会用person替代org.java.codelib.xstream.Person
同样对于field也可以使用alias:
xstream.aliasField("personId", Person.class, "id");
这样就会将Person中的id替换为
其他的还有aliasAttribute即将field作为attribute时并采用别名,当前前提是需要设置field作为attribute:
XStream xstream = new XStream(new StaxDriver()); xstream.alias("person", Person.class); xstream.useAttributeFor(Person.class, "id"); xstream.aliasAttribute("personId", "id"); xstream.toXML(p);
输出为:
robin 123 xxRoad chengdu
说到设置field作为attribute如果field是一个自定义对象,或者需要将Date之类的属性格式化输出,如本例中的Address该如何处理?这就是另外一个话题
需要说明的是以上在序列化为xml的时候使用了alias,那么在反序列化的时候同样需要这些相应的代码,不然可能会抛出UnknownFieldException
4、convertors
convertor的作用是在做序列化或反序列化的时候,将对象中的属性按照特定的形式输出或转化,在XStream 中默认初始化了大量的必要convertors,见http://xstream.codehaus.org/converters.html 或者在XStream.java中有方法setupConverters()。
自定义一个convertor需要两步:
1、实现Converter接口及相关方法:
public class DateConverter implements Converter { @Override public boolean canConvert(Class type) { return type.equals(Date.class); } @Override public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); writer.setValue(dateFormat.format((Date) source)); } @Override public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { return dateFormat.parse(reader.getValue()); } catch (ParseException e) { e.printStackTrace(); } return null; } }
2、在xstream中注册该convertor:
xstream.registerConverter(new DateConverter());
输出
1 robin 123 2013-02-17 15:12:53 xxRoad chengdu
当然,xstream针对Date也做了默认的实现,只不过默认输出为UTC格式
现在我们回到上面的问题,即将对象Address作为Person的属性,下面是一个convertor的实现:
public class PersonConverter implements Converter { @SuppressWarnings("rawtypes") @Override public boolean canConvert(Class type) { return type.equals(Person.class); } @Override public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { Person person = (Person) source; if (person != null) { Address address = person.getAddress(); if (address != null) { if (StringUtils.isNotBlank(address.getStreet())) { writer.addAttribute("street", address.getStreet()); } if (StringUtils.isNotBlank(address.getCity())) { writer.addAttribute("city", address.getCity()); } } //address if (person.getBirthday() != null) { writer.startNode("birthday"); context.convertAnother(person.getBirthday(), new DateConverter()); writer.endNode(); } //username if (person.getUsername() != null) { writer.startNode("username"); context.convertAnother(person.getUsername()); writer.endNode(); } //other fields } } @Override public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Person p = new Person(); Address address = new Address(); address.setCity(reader.getAttribute("city")); address.setStreet(reader.getAttribute("street")); p.setAddress(address); while (reader.hasMoreChildren()) { reader.moveDown(); if ("birthday".equals(reader.getNodeName())) { Date date = (Date) context.convertAnother(p, Date.class, new DateConverter()); p.setBirthday(date); } else if ("username".equals(reader.getNodeName())) { p.setUsername((String) context.convertAnother(p, String.class)); } //other fields reader.moveUp(); } return p; } }
其中序列化时输出:
2013-02-17 16:34:24 robin
当然如果作为fields的对象只有一个属性就简单得多了,在http://xstream.codehaus.org/alias-tutorial.html#attributes有例子可供参考
5、implicitCollections
考虑Person有列表属性:
private List addresses;
在序列化为xml时:
road_1 chengdu road_2 chengdu
当然有时候并不想要addresses,这就是XStream中的implicitCollections:对集合的属性在序列化是不想显示roottag。值需要很简单的处理:
XStream xstream = new XStream(new StaxDriver()); xstream.alias("person", Person.class); xstream.alias("address", Address.class); xstream.addImplicitCollection(Person.class, "addresses"); return xstream.toXML(formatPerson());
输出为: