Gson 对enum的序列化与反序列化配置

该博客详细介绍了如何通过Gson的TypeAdapterFactory自定义配置,实现Enum的序列化为序号或名称,并支持通过序号或名称反序列化。提供了一个通用接口SerializableEnum,允许对每个Enum类定制序列化和反序列化方法,提高灵活性。示例代码展示了如何注册和使用这些配置,便于在项目中应用。
摘要由CSDN通过智能技术生成

Gson 对enum的序列化与反序列化配置

前言

在网上找了一圈,都没有一个能够直接配置使用的Gson序列化enum配置。

对于enum序列化与反序列化的主要难点在于TypeAdapter在反序列化时无法获取enum的类型

基础序列化与反序列化(仅使用enum的ordinal与name)

此处提供的解决方案可以通过闭包的方式从TypeAdapterFactory实例中获取enum的class,在此放出一份最简单的增强Gson序列化与反序列化enum的方法。

(可选则将enum序列化为enum序号或名字,也可选通过enum的序号或名字或两者反序列化enum)

/**
 * 使用配置生成Gson对象
 * @author lty2008one
 * @date 2022/02/20
 */
private static Gson configure() {
	return new GsonBuilder()
		.registerTypeAdapterFactory(new TypeAdapterFactory() {
			@Override
			public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> token) {
				Class<? super T> rawType = token.getRawType();
				if(rawType.isEnum()) return new TypeAdapter<T>() {
					@Override
					public void write(JsonWriter out, T value) {
						if(value == null) out.nullValue();
						// 此处定义序列化为枚举的序号,也可换成name()序列化为名字,或自定义喜欢的格式
						else out.value(((Enum<T>) value).ordinal());
					}
					
					@Override
					public T read(JsonReader in) throws IOException {
	                    if(in.peek().equals(JsonToken.NULL)) {
                        	in.nextNull();
                            return null;
                        } else {
                            String value = in.nextString();
                            try {
                                Method values = type.getRawType().getMethod("values");
                                Enum[] invoke = (SerializableEnum[]) values.invoke(type.getClass());
                                for(int i = 0; i < invoke.length; i++) {
                                	// 通过序号反序列化
                                	if(Objects.equals(invoke[i].ordinal()+"", value)) return invoke[i];
                                	// 通过名字反序列化
                                	if(Objects.equals(invoke[i].name(), value)) return invoke[i];
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            } return null;
                        }
                    }
				} else return null;
			}
		}).create();
}

经过此配置后,Gson就可以序列化Enum为ordinal,也可以通过Enum的名字和序号反序列化Enum而非必须使用名字进行反序列化了

进阶序列化与反序列化(任意配置,可继承)

当然,对于大家来说,只做到这一层肯定还不太够用,我们可能需要的是针对一些特殊的Enum有各自的序列化方法,而且不用将每个Enum类都注册到TypeAdapter中

那么我们首先定义一个Enum的通用接口

/**
 * 实现此接口的必须为enum,否则可能会报错
 * 实现时,泛型E必须为实现类的类型
 * @author lty2008one
 * @date 2022/02/20
 */
public interface SerializableEnum<E> {

    /**
     * 默认将enum序列化为序号
     * @return
     */
    default String serializenum() {
        return ((Enum) this).ordinal() + "";
    }

    /**
     * 默认使用序号和名字进行反序列化
     * @param value
     * @return
     */
    default E deserializenum(String value) {
        if(value == null) return null;
        Class<? extends SerializableEnum> clazz = getClass();
        for (SerializableEnum enumConstant : clazz.getEnumConstants()) {
            Enum enumValue = (Enum) enumConstant;
            if(value.equals(enumValue.ordinal() + "")) return (E) enumConstant;
            if(value.equals(enumValue.name())) return (E) enumConstant;
        } return null;
    }

}

有这个通用接口在手,我们就可以对序列化与反序列化的方法进行严格管控了,此时,在Gson的配置项中如此注册即可。

private static Gson config() {
	List<String> allow = Arrays.asList("serializenum", "deserializenum");
    return new GsonBuilder()
        .registerTypeAdapterFactory(new TypeAdapterFactory() {
            @Override
            public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
                Class<? super T> rawType = type.getRawType();
                if (rawType.isEnum()) {
                    boolean pass = Stream.of(rawType.getMethods()).map(Method::getName)
                        .collect(Collectors.toList()).containsAll(allow);
                    if (pass) return new TypeAdapter<T>() {
                        @Override
                        public void write(JsonWriter out, T value) throws IOException {
                            if(value == null) out.nullValue();
                            else out.value(((SerializableEnum<T>) value).serializenum());
                        }

                        @Override
                        public T read(JsonReader in) throws IOException {
                            if(in.peek().equals(JsonToken.NULL)) {
                                in.nextNull();
                                return null;
                            } else {
                                String value = in.nextString();
                                try {
                                    Method values = type.getRawType().getMethod("values");
                                    SerializableEnum[] invoke = (SerializableEnum[]) values.invoke(type.getClass());
                                    return (T) invoke[0].deserializenum(value);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                } return null;
                            }
                        }
                    };
                } return null;
            }
        }).create();
}

此时,在任一实现了SerializableEnum接口的enum枚举类中重新实现serializenum和deserializenum方法即可快速更改此枚举的序列化与反序列化方式
例如

public enum Sex implements SerializableEnum<Sex> {
    FUTA(2, "扶她"),
    FEMALE(0, "女"),
    OTHER(3, "其他"),
    MALE(1, "男");

    private final int order;
    private final String desc;
    Sex(int order, String desc) {
        this.order = order;
        this.desc = desc;
    }


    @Override
    public String serializenum() {
        // 序列化为描述
        return desc;
    }

    @Override
    public Sex deserializenum(String value) {
        for (Sex sex : values()) {
            // 仅使用自定义的order值进行反序列化
            if(Objects.equals(sex.order + "", value)) return sex;
        } return null;
    }
}

在序列化时会被序列化为描述,反序列化时则会根据定义的order值找到对应的项

欢迎各位学习使用,转载或引用请附带原文发布连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值