XStream是一个将对象序列化为xml并解析xml为对象的框架,主页位于http://xstream.codehaus.org。使用非常简单
引入依赖:
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.2</version>
</dependency>
需要的依赖
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对象即可:
Java代码
- XStream xstream = new XStream();
- //采用这个构造器默认需要依赖:xstream-[version].jar, xpp3-[version].jar and xmlpull-[version].jar
或者采用DOM的方式解析:
Java代码
- XStream xstream = new XStream(new DomDriver());
- //此时不需要XPP3
或者基于事件的StAX
Java代码
- XStream xstream = new XStream(new StaxDriver());
- //如果采用Java 6,也不需要xpp3.将采用默认的JAXB
Java代码
- //对象序列化为xml
- xstream.toXML(Object)
-
- //xml反序列化为对象
- xstream.fromXML(xml)
一个例子:
Java代码
- Person p = new Person();
- p.setId(1);
- p.setUsername("robin");
- p.setPassword("123");
- p.setAddress(new Address("xxRoad", "chengdu"));
- xstream.toXML(p);
输出为:
Xml代码
- <org.java.codelib.xstream.Person>
- <id>1</id>
- <username>robin</username>
- <password>123</password>
- <address>
- <street>xxRoad</street>
- <city>chengdu</city>
- </address>
- </org.java.codelib.xstream.Person>
3、alias
这里可以看到生成的xml中root element名字为class,如果需要修改就需要用到
Java代码
- xstream.alias("person", Person.class);
这样就会用person替代org.java.codelib.xstream.Person
同样对于field也可以使用alias:
Java代码
- xstream.aliasField("personId", Person.class, "id");
这样就会将Person中的id替换为<personId>1</personId>
其他的还有aliasAttribute即将field作为attribute时并采用别名,当前前提是需要设置field作为attribute:
Java代码
- XStream xstream = new XStream(new StaxDriver());
- xstream.alias("person", Person.class);
- xstream.useAttributeFor(Person.class, "id");
- xstream.aliasAttribute("personId", "id");
- xstream.toXML(p);
输出为:
Xml代码
- <?xml version="1.0" ?><person personId="1"><username>robin</username><password>123</password><address><street>xxRoad</street><city>chengdu</city></address></person>
说到设置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接口及相关方法:
Java代码
- 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:
Java代码
- xstream.registerConverter(new DateConverter());
输出
Xml代码
- <?xml version="1.0" ?><person><id>1</id><username>robin</username><password>123</password><birthday>2013-02-17 15:12:53</birthday><address><street>xxRoad</street><city>chengdu</city></address></person>
当然,xstream针对Date也做了默认的实现,只不过默认输出为UTC格式
现在我们回到上面的问题,即将对象Address作为Person的属性,下面是一个convertor的实现:
Java代码
- 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;
- }
- }
其中序列化时输出:
Xml代码
- <?xml version="1.0" ?><person street="xxRoad" city="chengdu"><birthday>2013-02-17 16:34:24</birthday><username>robin</username></person>
当然如果作为fields的对象只有一个属性就简单得多了,在http://xstream.codehaus.org/alias-tutorial.html#attributes有例子可供参考
5、implicitCollections
考虑Person有列表属性:
Java代码
- private List<Address> addresses;
在序列化为xml时:
Xml代码
- <?xml version="1.0" ?><person><addresses><address><street>road_1</street><city>chengdu</city></address><address><street>road_2</street><city>chengdu</city></address></addresses></person>
当然有时候并不想要addresses,这就是XStream中的implicitCollections:对集合的属性在序列化是不想显示roottag。值需要很简单的处理:
Java代码
- 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());
输出为:
Xml代码
- <?xml version="1.0" ?><person><address><street>road_1</street><city>chengdu</city></address><address><street>road_2</street><city>chengdu</city></address></person>
6、annotation
以上说的内容都支持annotation:
Java代码
- @XStreamAlias("person")
- public class Person {
-
- @XStreamAlias("personId")
- @XStreamAsAttribute
- private Integer id;
- private String username;
- private String password;
- @XStreamConverter(DateConverter.class)
- private Date birthday;
- private Address address;
- @XStreamImplicit(itemFieldName = "address")
- private List<Address> addresses;
- }
Java代码
- @XStreamAlias("person")
- public class Address {
-
- @XStreamAsAttribute
- private String street;
- @XStreamAsAttribute
- private String city;
- }
需要加上autodetectAnnotations(true)
Java代码
- XStream xstream = new XStream(new StaxDriver());
- xstream.autodetectAnnotations(true);
- return xstream.toXML(p);
输出:
Xml代码
- <?xml version="1.0" ?><person personId="1"><username>robin</username><password>123</password><birthday>2013-02-17 17:08:00</birthday><address street="road_1" city="chengdu"></address><address street="road_2" city="chengdu"></address></person>
7、其他
xstream提供了对json的解析以及持久化(文件系统)的支持,这里就不再介绍了