XStream

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等)工厂

其他可供选择的Xpp3DOM4J

 

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);

 输出为:

<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,如果需要修改就需要用到

xstream.alias("person", Person.class);

这样就会用person替代org.java.codelib.xstream.Person

同样对于field也可以使用alias:

xstream.aliasField("personId", Person.class, "id");

这样就会将Person中的id替换为<personId>1</personId>

其他的还有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);

输出为:

<?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接口及相关方法:

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());

 输出

<?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的实现:

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 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有列表属性:

private List<Address> addresses;

 在序列化为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。值需要很简单的处理:

        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 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:

@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;
}
@XStreamAlias("person")
public class Address {

    @XStreamAsAttribute
    private String street;
    @XStreamAsAttribute
    private String city;
}

需要加上autodetectAnnotations(true)

XStream xstream = new XStream(new StaxDriver());
xstream.autodetectAnnotations(true);
return xstream.toXML(p);

输出:

<?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的解析以及持久化(文件系统)的支持,这里就不再介绍了

 

 

你可能感兴趣的:(xml,dom,xstream,StAX)