【工具类】理解 TypeUtils 类:深入解析 FastJSON 的类型转换工具

引言

TypeUtils 是 FastJSON 库中的一个重要工具类,主要用于处理各种类型的转换和解析。FastJSON 是阿里巴巴开源的一个高性能 JSON 解析和生成库,广泛应用于 Java 应用中。本文将详细介绍 TypeUtils 类的主要功能和实现细节,帮助读者更好地理解和使用这一工具类。

1. 类概述

TypeUtils 类提供了多种静态方法,用于处理不同类型之间的转换、反射操作以及一些辅助功能。这些方法在 FastJSON 的序列化和反序列化过程中起着至关重要的作用。

2. 主要功能
2.1 类型转换

TypeUtils 提供了多种类型转换方法,用于将对象转换为目标类型。这些方法包括但不限于:

  • castToXXX 系列方法:将对象转换为特定的基本类型(如 int, long, double 等)。
  • convertValue 方法:将对象转换为指定的目标类型。
示例代码
java 复制代码
public static Integer castToInt(Object value) {
    if (value == null) {
        return null;
    }
    if (value instanceof Integer) {
        return (Integer) value;
    }
    if (value instanceof Number) {
        return ((Number) value).intValue();
    }
    if (value instanceof String) {
        String strVal = (String) value;
        if (strVal.length() == 0 || "null".equals(strVal)) {
            return null;
        }
        return Integer.parseInt(strVal);
    }
    throw new JSONException("can not cast to int, value : " + value);
}
2.2 反射操作

TypeUtils 提供了一系列反射相关的操作,用于获取类的字段、方法等信息。这些方法包括:

  • getField:获取类的字段。
  • getDeclaredFields:获取类的所有声明字段。
  • getMethods:获取类的所有方法。
示例代码
java 复制代码
public static Field getField(Class<?> clazz, String fieldName) {
    if (clazz == null) {
        return null;
    }
    try {
        return clazz.getDeclaredField(fieldName);
    } catch (NoSuchFieldException e) {
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            return getField(superClass, fieldName);
        }
    }
    return null;
}
2.3 类型解析

TypeUtils 提供了多种类型解析方法,用于处理泛型、数组、集合等复杂类型的解析。这些方法包括:

  • getCollectionItemType:获取集合类型的元素类型。
  • getActualType:获取泛型的实际类型。
  • getRawClass:获取类型的原始类。
示例代码
java 复制代码
public static Class<?> getCollectionItemType(Type fieldType) {
    if (fieldType instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) fieldType;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        if (actualTypeArguments.length == 1) {
            return getRawClass(actualTypeArguments[0]);
        }
    }
    return Object.class;
}
2.4 辅助功能

TypeUtils 还提供了一些辅助功能,如日期和时间的解析、字符串处理等。

示例代码
java 复制代码
public static java.sql.Timestamp castToTimestamp(Object value) {
    if (value == null) {
        return null;
    }
    if (value instanceof java.sql.Timestamp) {
        return (java.sql.Timestamp) value;
    }
    if (value instanceof Date) {
        return new java.sql.Timestamp(((Date) value).getTime());
    }
    if (value instanceof String) {
        String strVal = (String) value;
        long longValue = 0;
        if (strVal.length() == 23) {
            int year = num(strVal.charAt(0), strVal.charAt(1), strVal.charAt(2), strVal.charAt(3));
            int month = num(strVal.charAt(5), strVal.charAt(6));
            int day = num(strVal.charAt(8), strVal.charAt(9));
            int hour = num(strVal.charAt(11), strVal.charAt(12));
            int minute = num(strVal.charAt(14), strVal.charAt(15));
            int second = num(strVal.charAt(17), strVal.charAt(18));
            int nanos = num(strVal.charAt(20), strVal.charAt(21), strVal.charAt(22), strVal.charAt(23), strVal.charAt(24), strVal.charAt(25), strVal.charAt(26), strVal.charAt(27), strVal.charAt(28));
            return new java.sql.Timestamp(year - 1900, month - 1, day, hour, minute, second, nanos);
        }
        if (isNumber(strVal)) {
            longValue = Long.parseLong(strVal);
        } else {
            JSONScanner scanner = new JSONScanner(strVal);
            if (scanner.scanISO8601DateIfMatch(false)) {
                longValue = scanner.getCalendar().getTime().getTime();
            } else {
                throw new JSONException("can not cast to Timestamp, value : " + strVal);
            }
        }
        if (longValue <= 0) {
            throw new JSONException("can not cast to Timestamp, value : " + value);
        }
        return new java.sql.Timestamp(longValue);
    }
    throw new JSONException("can not cast to Timestamp, value : " + value);
}
3. 实现细节
3.1 类型转换

类型转换方法主要通过 instanceof 操作符和 Class 类的方法来判断对象的类型,并进行相应的转换。对于复杂的类型转换,如日期和时间的解析,TypeUtils 使用了正则表达式和 JSONScanner 等工具类来处理。

3.2 反射操作

反射操作主要通过 Class 类的 getDeclaredFieldgetDeclaredMethods 等方法来获取类的字段和方法信息。为了处理继承关系,TypeUtils 递归地查找父类的字段和方法。

3.3 类型解析

类型解析方法主要通过 ParameterizedTypeGenericArrayType 等接口来处理泛型和数组类型。TypeUtils 提供了多种辅助方法来获取泛型的实际类型和原始类。

4. 使用场景

TypeUtils 类在 FastJSON 的序列化和反序列化过程中扮演着重要角色。以下是一些常见的使用场景:

  • 对象转换:将 JSON 字符串转换为 Java 对象时,需要将不同的 JSON 值转换为对应的 Java 类型。
  • 反射操作:在处理复杂的对象结构时,需要通过反射获取类的字段和方法信息。
  • 类型解析:在处理泛型和集合类型时,需要解析类型参数以确保正确性。
5. 结论

TypeUtils 类是 FastJSON 库中的一个重要工具类,提供了丰富的类型转换、反射操作和类型解析功能。通过本文的介绍,希望读者能够更好地理解和使用 TypeUtils 类,从而在实际开发中更加高效地处理各种类型转换和反射操作。

附录:相关代码片段
java 复制代码
public class TypeUtils {
    private static final Pattern NUMBER_WITH_TRAILING_ZEROS_PATTERN = Pattern.compile("\\.0*$");
    public static boolean compatibleWithJavaBean = false;

    public static Integer castToInt(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Integer) {
            return (Integer) value;
        }
        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        if (value instanceof String) {
            String strVal = (String) value;
            if (strVal.length() == 0 || "null".equals(strVal)) {
                return null;
            }
            return Integer.parseInt(strVal);
        }
        throw new JSONException("can not cast to int, value : " + value);
    }

    public static Field getField(Class<?> clazz, String fieldName) {
        if (clazz == null) {
            return null;
        }
        try {
            return clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                return getField(superClass, fieldName);
            }
        }
        return null;
    }

    public static Class<?> getCollectionItemType(Type fieldType) {
        if (fieldType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) fieldType;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            if (actualTypeArguments.length == 1) {
                return getRawClass(actualTypeArguments[0]);
            }
        }
        return Object.class;
    }

    public static java.sql.Timestamp castToTimestamp(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof java.sql.Timestamp) {
            return (java.sql.Timestamp) value;
        }
        if (value instanceof Date) {
            return new java.sql.Timestamp(((Date) value).getTime());
        }
        if (value instanceof String) {
            String strVal = (String) value;
            long longValue = 0;
            if (strVal.length() == 23) {
                int year = num(strVal.charAt(0), strVal.charAt(1), strVal.charAt(2), strVal.charAt(3));
                int month = num(strVal.charAt(5), strVal.charAt(6));
                int day = num(strVal.charAt(8), strVal.charAt(9));
                int hour = num(strVal.charAt(11), strVal.charAt(12));
                int minute = num(strVal.charAt(14), strVal.charAt(15));
                int second = num(strVal.charAt(17), strVal.charAt(18));
                int nanos = num(strVal.charAt(20), strVal.charAt(21), strVal.charAt(22), strVal.charAt(23), strVal.charAt(24), strVal.charAt(25), strVal.charAt(26), strVal.charAt(27), strVal.charAt(28));
                return new java.sql.Timestamp(year - 1900, month - 1, day, hour, minute, second, nanos);
            }
            if (isNumber(strVal)) {
                longValue = Long.parseLong(strVal);
            } else {
                JSONScanner scanner = new JSONScanner(strVal);
                if (scanner.scanISO8601DateIfMatch(false)) {
                    longValue = scanner.getCalendar().getTime().getTime();
                } else {
                    throw new JSONException("can not cast to Timestamp, value : " + strVal);
                }
            }
            if (longValue <= 0) {
                throw new JSONException("can not cast to Timestamp, value : " + value);
            }
            return new java.sql.Timestamp(longValue);
        }
        throw new JSONException("can not cast to Timestamp, value : " + value);
    }

    private static int num(char c0, char c1) {
        if (c0 >= '0' && c0 <= '9' && c1 >= '0' && c1 <= '9') {
            return (c0 - '0') * 10 + (c1 - '0');
        }
        return -1;
    }

    private static int num(char c0, char c1, char c2, char c3) {
        if (c0 >= '0' && c0 <= '9' && c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9' && c3 >= '0' && c3 <= '9') {
            return (c0 - '0') * 1000 + (c1 - '0') * 100 + (c2 - '0') * 10 + (c3 - '0');
        }
        return -1;
    }

    private static int num(char c0, char c1, char c2, char c3, char c4, char c5, char c6, char c7, char c8) {
        if (c0 >= '0' && c0 <= '9' && c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9' && c3 >= '0' && c3 <= '9' && c4 >= '0' && c4 <= '9' && c5 >= '0' && c5 <= '9' && c6 >= '0' && c6 <= '9' && c7 >= '0' && c7 <= '9' && c8 >= '0' && c8 <= '9') {
            return (c0 - '0') * 100000000 + (c1 - '0') * 10000000 + (c2 - '0') * 1000000 + (c3 - '0') * 100000 + (c4 - '0') * 10000 + (c5 - '0') * 1000 + (c6 - '0') * 100 + (c7 - '0') * 10 + (c8 - '0');
        }
        return -1;
    }

    public static boolean isNumber(String str) {
        for (int i = 0; i < str.length(); ++i) {
            char ch = str.charAt(i);
            if (ch == '+' || ch == '-') {
                if (i != 0) {
                    return false;
                }
            } else if (ch < '0' || ch > '9') {
                return false;
            }
        }
        return true;
    }
}
相关推荐
武子康5 分钟前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神37 分钟前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
qq_327342731 小时前
Java实现离线身份证号码OCR识别
java·开发语言
阿龟在奔跑2 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF2 小时前
m个数 生成n个数的所有组合 详解
java·递归
代码小鑫2 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖3 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶3 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
周全全3 小时前
Spring Boot + Vue 基于 RSA 的用户身份认证加密机制实现
java·vue.js·spring boot·安全·php
uzong4 小时前
一个 IDEA 老鸟的 DEBUG 私货之多线程调试
java·后端