【工具类】理解 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;
    }
}
相关推荐
潜意识起点9 分钟前
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
java·spring boot·后端
mxbb.10 分钟前
单点Redis所面临的问题及解决方法
java·数据库·redis·缓存
云和数据.ChenGuang35 分钟前
《XML》教案 第1章 学习XML基础
xml·java·学习
王·小白攻城狮·不是那么帅的哥·天文43 分钟前
Java操作Xml
xml·java
发飙的蜗牛'1 小时前
23种设计模式
android·java·设计模式
music0ant1 小时前
Idean 处理一个项目引用另外一个项目jar 但jar版本低的问题
java·pycharm·jar
陈大爷(有低保)1 小时前
logback日志控制台打印与写入文件
java
繁川1 小时前
深入理解Spring AOP
java·后端·spring
Am心若依旧4091 小时前
[c++进阶(三)]单例模式及特殊类的设计
java·c++·单例模式
ZHOUPUYU4 小时前
最新 neo4j 5.26版本下载安装配置步骤【附安装包】
java·后端·jdk·nosql·数据库开发·neo4j·图形数据库