zoukankan      html  css  js  c++  java
  • 关于类型转换这件事

    有同事和我讨论了一个问题:怎样把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
  • 相关阅读:
    POJ2686 Traveling by Stagecoach(状压DP+SPFA)
    POJ3250 Bad Hair Day(单调栈)
    POJ3493 Largest Submatrix of All 1’s(单调栈)
    UVA 10160 Servicing Stations(状态压缩+迭代加深)
    POJ 2187 Beauty Contest
    HDU 6017 Girls Love 233(多态继承DP)
    POJ 2932 Coneology(扫描线)
    POJ 1127 Jack Straws (计算几何)
    挑战程序设计竞赛 3.5 借助水流解决问题的网络流
    AOJ 2230 How to Create a Good Game(费用流)
  • 原文地址:https://www.cnblogs.com/chanedi/p/5890775.html
Copyright © 2011-2022 走看看