概述

我们在开发的过程中,有的时候需要转换一些自定义类型,此时默认的映射方式可能无法满足需要。

XStream为我们提供了丰富的扩展,用户可以实现自己的转换器,然后调用registerConverter方法注册自定义的转换器。

实现自定义的转换器很简单,只需要实现XStream提供的Converter接口并实现其方法即可。


示例

我们在上个案例中的代码

package com.xgj.oxm.xstream.quickDemo.convert;

import java.text.ParseException;
import java.util.Date;
import java.util.Locale;

import com.thoughtworks.xstream.XStream;
import com.xgj.oxm.xstream.quickDemo.domain.LoginLog;
import com.xgj.oxm.xstream.quickDemo.domain.User;

public class XStreamConverterDemo {

    private static XStream xstream;

    static {
        // 创建一个Xstream实例,使用默认的XPP解析器
        xstream = new XStream();

        // (1)设置类别名,修改默认的全限定名的名称
        xstream.alias("user", User.class);
        xstream.alias("loginLog", LoginLog.class);

        // (2)设置类成员别名 <id>1</id> 改为<userId>1</userId>
        xstream.aliasField("userId", User.class, "id");

        // (3)把LoginLog的userId属性视为xml属性,默认为xml的元素
        xstream.aliasAttribute(LoginLog.class, "userId", "id");
        xstream.useAttributeFor(LoginLog.class, "userId");

        // (4)去掉集合类型生成XML的父节点,即忽略xml中的<logs></logs>标记
        xstream.addImplicitCollection(User.class, "logs");


    }

    /**
     * 
     * 
     * @Title: getUser
     * 
     * @Description: 初始化转换对象
     * 
     * @return
     * 
     * @return: User
     * @throws ParseException
     */
    public static User getUser() throws ParseException {
        LoginLog log = new LoginLog();
        log.setIp("127.0.0.1");
        log.setLoginLogId(99);
        log.setUserId(1);
        log.setLoginDate(new Date());

        LoginLog log2 = new LoginLog();
        log2.setIp("192.168.1.1");
        log2.setLoginLogId(22);
        log2.setUserId(2);
        log2.setLoginDate(new Date());

        User user = new User();
        user.setId(1);
        user.setUserName("Artisan");
        user.setPassword("artisan");
        user.setCredits(1000);
        user.setLastVisit(new Date());

        user.addLoginLog(log);
        user.addLoginLog(log2);
        return user;
    }

    /**
     * 
     * 
     * @Title: objectToXml
     * 
     * @Description: Java对象转换成XML
     * 
     * @throws Exception
     * 
     * @return: void
     */
    public static void objectToXml() throws Exception {
        // 获取转换的User对象实例
        User user = getUser();
        // 输出内容到控制台,查看一下
        System.out.println(xstream.toXML(user));
    }



    public static void main(String[] args) throws Exception {
        objectToXml();
    }
}

输出如下

<user>
  <userId>1</userId>
  <userName>Artisan</userName>
  <password>artisan</password>
  <credits>1000</credits>
  <lastVisit>2017-12-06 12:32:06.504 UTC</lastVisit>
  <loginLog id="1">
    <loginLogId>99</loginLogId>
    <ip>127.0.0.1</ip>
    <loginDate>2017-12-06 12:32:06.504 UTC</loginDate>
  </loginLog>
  <loginLog id="2">
    <loginLogId>22</loginLogId>
    <ip>192.168.1.1</ip>
    <loginDate>2017-12-06 12:32:06.504 UTC</loginDate>
  </loginLog>
</user>

可以看到时间格式如下:

2017-12-06 12:32:06.504 UTC

UTC与格林尼治平均时(GMT, Greenwich Mean Time)一样,都与英国伦敦的本地时相同,UTC + 时区差=本地时间

如果我们希望显示北京时间呢?

就需要用到转换器了

package com.xgj.oxm.xstream.quickDemo.convert;

import java.text.SimpleDateFormat;
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;
    }

    /**
     * 重写该方法,判断需要转换的类型
     */
    @Override
    public boolean canConvert(Class type) {
        return Date.class.isAssignableFrom(type);
    }

    /**
     * 重写该方法,编写Java 到 XML 的转换逻辑
     */
    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer,
            MarshallingContext context) {

        // DateFormat format = DateFormat.getDateInstance(DateFormat.DATE_FIELD,
        // this.locale);

        SimpleDateFormat format = new SimpleDateFormat(
                "yyyy-MM-dd HH:mm:ss", this.locale);

        writer.setValue(format.format(source));

    }

    /**
     * 重写该方法,编写XML 到 Java 的转换逻辑
     */
    @Override
    public Object unmarshal(HierarchicalStreamReader reader,
            UnmarshallingContext context) {

        GregorianCalendar calendar = new GregorianCalendar();

        // DateFormat format = DateFormat.getDateInstance(DateFormat.DATE_FIELD,
        // this.locale);

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",
                this.locale);

        try {
            calendar.setTime(format.parse(reader.getValue()));
        } catch (Exception e) {
            throw new ConversionException(e.getMessage(), e);
        }

        return calendar.getGregorianChange();
    }

}

解析

  • 通过 canConvert 方法来判断转换逻辑

  • 通过marshal方法,完成对象编组(对象转XML)操作的处理逻辑

  • 通过unmarshal方法,完成对象f反编组(XML转对象)操作的处理逻辑

  • 最后调用registerConverter方法注册自定义的转换器


修改生成xml的代码注册转换器,完整代码如下

package com.xgj.oxm.xstream.quickDemo.convert;

import java.text.ParseException;
import java.util.Date;
import java.util.Locale;

import com.thoughtworks.xstream.XStream;
import com.xgj.oxm.xstream.quickDemo.domain.LoginLog;
import com.xgj.oxm.xstream.quickDemo.domain.User;

public class XStreamConverterDemo {

    private static XStream xstream;

    static {
        // 创建一个Xstream实例,使用默认的XPP解析器
        xstream = new XStream();

        // (1)设置类别名,修改默认的全限定名的名称
        xstream.alias("user", User.class);
        xstream.alias("loginLog", LoginLog.class);

        // (2)设置类成员别名 <id>1</id> 改为<userId>1</userId>
        xstream.aliasField("userId", User.class, "id");

        // (3)把LoginLog的userId属性视为xml属性,默认为xml的元素
        xstream.aliasAttribute(LoginLog.class, "userId", "id");
        xstream.useAttributeFor(LoginLog.class, "userId");

        // (4)去掉集合类型生成XML的父节点,即忽略xml中的<logs></logs>标记
        xstream.addImplicitCollection(User.class, "logs");

        // 注册转换器
        xstream.registerConverter(new DateConverter(Locale.CHINESE));
    }

    /**
     * 
     * 
     * @Title: getUser
     * 
     * @Description: 初始化转换对象
     * 
     * @return
     * 
     * @return: User
     * @throws ParseException
     */
    public static User getUser() throws ParseException {
        LoginLog log = new LoginLog();
        log.setIp("127.0.0.1");
        log.setLoginLogId(99);
        log.setUserId(1);
        log.setLoginDate(new Date());

        LoginLog log2 = new LoginLog();
        log2.setIp("192.168.1.1");
        log2.setLoginLogId(22);
        log2.setUserId(2);
        log2.setLoginDate(new Date());

        User user = new User();
        user.setId(1);
        user.setUserName("Artisan");
        user.setPassword("artisan");
        user.setCredits(1000);
        user.setLastVisit(new Date());

        user.addLoginLog(log);
        user.addLoginLog(log2);
        return user;
    }

    /**
     * 
     * 
     * @Title: objectToXml
     * 
     * @Description: Java对象转换成XML
     * 
     * @throws Exception
     * 
     * @return: void
     */
    public static void objectToXml() throws Exception {
        // 获取转换的User对象实例
        User user = getUser();
        // 输出内容到控制台,查看一下
        System.out.println(xstream.toXML(user));
    }



    public static void main(String[] args) throws Exception {
        objectToXml();
    }
}

输出

<user>
  <userId>1</userId>
  <userName>Artisan</userName>
  <password>artisan</password>
  <credits>1000</credits>
  <lastVisit>2017-12-06 20:34:42</lastVisit>
  <loginLog id="1">
    <loginLogId>99</loginLogId>
    <ip>127.0.0.1</ip>
    <loginDate>2017-12-06 20:34:42</loginDate>
  </loginLog>
  <loginLog id="2">
    <loginLogId>22</loginLogId>
    <ip>192.168.1.1</ip>
    <loginDate>2017-12-06 20:34:42</loginDate>
  </loginLog>
</user>

可以看到时间已经是 2017-12-06 20:34:42 北京时间了。 转换器起作用了。


示例源码

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster