关于类型转换这件事

有同事和我讨论了一个问题:怎样把Date转换成正确的格式给前端?当然可以手动转换,但是他更期望通过框架配置的方式解决。关于这个问题有两种思路:1、在orm框架中转换;2、在json框架中转换。在这里我想分享一下自己的理解和经验。

 

谁可以做?

类型转换不是什么神奇的事,看源码就会发现,它只是一堆的if else。如果是基本类型怎么转,如果是枚举类怎么转,如果是日期怎么转,如果是普通Object怎么转。也就是各种的type handler。只要做了类型转换这件事,正常一点的框架,很自然都会支持配置这种handler,以及新增handler。

FastJson的代码片段

        if (writer == null) {
            if (Map.class.isAssignableFrom(clazz)) {
                config.put(clazz, MapSerializer.instance);
            } else if (List.class.isAssignableFrom(clazz)) {
                config.put(clazz, ListSerializer.instance);
            } else if (Collection.class.isAssignableFrom(clazz)) {
                config.put(clazz, CollectionSerializer.instance);
            } else if (Date.class.isAssignableFrom(clazz)) {
                config.put(clazz, DateSerializer.instance);
            } else if (JSONAware.class.isAssignableFrom(clazz)) {
                config.put(clazz, JSONAwareSerializer.instance);
            } else if (JSONStreamAware.class.isAssignableFrom(clazz)) {
                config.put(clazz, JSONStreamAwareSerializer.instance);
            } else if (clazz.isEnum() || (clazz.getSuperclass() != null && clazz.getSuperclass().isEnum())) {
                config.put(clazz, EnumSerializer.instance);
            } else if (clazz.isArray()) {
                Class<?> componentType = clazz.getComponentType();
                ObjectSerializer compObjectSerializer = getObjectWriter(componentType);
                config.put(clazz, new ArraySerializer(componentType, compObjectSerializer));
            } else if (Throwable.class.isAssignableFrom(clazz)) {
                config.put(clazz, new ExceptionSerializer(clazz));
            } else if (TimeZone.class.isAssignableFrom(clazz)) {
                config.put(clazz, TimeZoneCodec.instance);
            } else if (Appendable.class.isAssignableFrom(clazz)) {
                config.put(clazz, AppendableSerializer.instance);
            } else if (Charset.class.isAssignableFrom(clazz)) {
                config.put(clazz, CharsetCodec.instance);
            } else if (Enumeration.class.isAssignableFrom(clazz)) {
                config.put(clazz, EnumerationSeriliazer.instance);
            } else if (Calendar.class.isAssignableFrom(clazz)) {
                config.put(clazz, CalendarCodec.instance);
            } else if (Clob.class.isAssignableFrom(clazz)) {
                config.put(clazz, ClobSeriliazer.instance);
            } else {
                boolean isCglibProxy = false;
                boolean isJavassistProxy = false;
                for (Class<?> item : clazz.getInterfaces()) {
                    if (item.getName().equals("net.sf.cglib.proxy.Factory")) {
                        isCglibProxy = true;
                        break;
                    } else if (item.getName().equals("javassist.util.proxy.ProxyObject")) {
                        isJavassistProxy = true;
                        break;
                    }
                }

                if (isCglibProxy || isJavassistProxy) {
                    Class<?> superClazz = clazz.getSuperclass();

                    ObjectSerializer superWriter = getObjectWriter(superClazz);
                    config.put(clazz, superWriter);
                    return superWriter;
                }

                if (Proxy.isProxyClass(clazz)) {
                    config.put(clazz, config.createJavaBeanSerializer(clazz));
                } else {
                    config.put(clazz, config.createJavaBeanSerializer(clazz));
                }
            }
View Code

 

ORM or JSON?

java bean中的字段,反映的应该是该字段的最原始的类型。日期就应该是Date,枚举值就应该是枚举类型。如何把它在数据库中正确地存取,是orm框架的事;如何正确地转换成字符串或者从字符串解析,是json框架的事。所以针对“把Date转换成正确的格式给前端”这件事,我理解还是由json框架来做比较合适。

 

GSON

gson要自定义配置可通过GsonBuilder来构造Gson对象。

示例代码

        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
        String jsonString = gson.toJson(eventProcess);

 

FASTJSON

阿里的fastjson的自定义转换格式的处理方式更为简单明了,只需使用com.alibaba.fastjson.annotation.JSONField注解。

示例代码

    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

 

Mybatis

Mybatis可以在mybatis-config.xml中定义自己的typeHandlers。下面的代码示例中,java bean的字段为枚举类型,数据库存储为数字,使用枚举对象的getOrder方法值进行转换。

mybatis-config.xml

    <typeHandlers>
        <typeHandler handler="com.common.mybatis.EnumTypeOrderHandler" javaType="com.massage.enumType.OrderStatus"/>
    </typeHandlers>

com.common.mybatis.EnumTypeOrderHandler

public class EnumTypeOrderHandler<E extends Enum<E>> extends BaseTypeHandler<E> {

    private Class<E> type;
    private final E[] enums;

    public EnumTypeOrderHandler(Class<E> type) {
        if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
        this.type = type;
        this.enums = type.getEnumConstants();
        if (this.enums == null) throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
    }


    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        int order = parameter.ordinal();
        try {
            Method getOrder = type.getMethod("getOrder");
            order = (int) getOrder.invoke(parameter);
        } catch (Exception e) {
        }
        ps.setInt(i, order);
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int order = rs.getInt(columnName);
        if (rs.wasNull()) {
            return null;
        } else {
            return parseOrder(order);
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int order = rs.getInt(columnIndex);
        if (rs.wasNull()) {
            return null;
        } else {
            return parseOrder(order);
        }
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int order = cs.getInt(columnIndex);
        if (cs.wasNull()) {
            return null;
        } else {
            return parseOrder(order);
        }
    }

    private E parseOrder(int order) throws SQLException {
        try {
            for (E anEnum : enums) {
                try {
                    Method getOrder = type.getMethod("getOrder");
                    int anOrder = (int) getOrder.invoke(anEnum);
                    if (anOrder == order) {
                        return anEnum;
                    }
                } catch (Exception e) {
                    return enums[order];
                }
            }
        } catch (Exception ex) {
            throw new IllegalArgumentException("Cannot convert " + order + " to " + type.getSimpleName() + " by ordinal value.", ex);
        }
        throw new IllegalArgumentException("Cannot convert " + order + " to " + type.getSimpleName() + " by ordinal value.");
    }

}
View Code

 

Spring

说到Spring,大家想到的就是ioc和aop。然而spring并不只有ioc和aop。这些年来Spring家出了很多产品,都是基于Spring核心API,也都天然地扩展性相当之强。Spring似乎提供了做一个基础框架所需的一切,包括类型转换(参见http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#validation一章)。比如,SpringMVC接收请求参数转为bean,Spring Batch的Reader读取记录为bean,都用到了PropertyEditor。

自定义TimeEditor

public class TimeEditor extends PropertyEditorSupport {

    private String format = "H:mm";

    public TimeEditor() {
    }

    public TimeEditor(String format) {
        this.format = format;
    }

    public void setAsText(String text) {
        DateFormat df = new SimpleDateFormat(format);
        try {
            Date date = df.parse(text);
            Time time = new Time(date.getTime());
            setValue(time);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

}
View Code

在Controller中使用自定义PropertyEditor来解析时间格式

    public Entity parseModel(HttpServletRequest request) {
        Object o = null;
        try {
            o = getEntityClass().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("无法创建类的实例:" + getEntityClass());
        }
        DataBinder dataBinder = new DataBinder(o);
        dataBinder.registerCustomEditor(Time.class, new TimeEditor());
        MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
        dataBinder.bind(mpvs);

        return (Entity) o;
    }
View Code

 

posted @ 2016-09-24 15:23  爱上飞飞的面码  阅读(330)  评论(0编辑  收藏  举报