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
  • 相关阅读:
    泛微云桥e-Bridge 目录遍历,任意文件读取
    (CVE-2020-8209)XenMobile-控制台存在任意文件读取漏洞
    selenium 使用初
    将HTML文件转换为MD文件
    Python对word文档进行操作
    使用java安装jar包出错,提示不是有效的JDK java主目录
    Windows server 2012安装VM tools异常解决办法
    ifconfig 命令,改变主机名,改DNS hosts、关闭selinux firewalld netfilter 、防火墙iptables规则
    iostat iotop 查看硬盘的读写、 free 查看内存的命令 、netstat 命令查看网络、tcpdump 命令
    使用w uptime vmstat top sar nload 等命令查看系统负载
  • 原文地址:https://www.cnblogs.com/chanedi/p/5890775.html
Copyright © 2011-2022 走看看