Solon v3.7.1

snack4 - Json 序列化之枚举的编解码

</> markdown

枚举(Enum)看是一个值,但又可以是各种不同的值。比如有 ordinal,有 name,还可以有结构。之于序列化,用况就特别多。特此专门作说明。

  • 主要的编码控制方式有:
方式描述编码(序列化)效果影响范围
A默认输出枚举 ordinal执行相关的所有枚举
B1选项特性 Write_EnumUsingName输出枚举 name执行相关的所有枚举
B2选项特性 Write_EnumUsingToString输出 toString() 结果同上
B3选项特性 Write_EnumShapeAsObject如果有字段?输出 json object 风格同上
C1类型特性 Write_EnumUsingName输出枚举 name当前类型
C2类型特性 Write_EnumUsingToString输出 toString() 结果同上
C3类型特性 Write_EnumShapeAsObject如果有字段?输出 json object 风格同上
D1类型字段 添加 @ONodeAttr 注解输出字段值当前类型
E1类型 自定义编解码输出定制值当前类型
F1值字段特性 Write_EnumUsingName输出枚举 name当前字段
F2值字段特性 Write_EnumUsingToString输出 toString() 结果同上
F3值字段特性 Write_EnumShapeAsObject如果有字段?输出 json object 风格同上

其中:类型特性和值字段特性,通过注解 @ONodeAttr(features=...) 附加特性。

  • 主要的解码控制方式有:
方式描述编码(序列化)效果影响范围
X1默认可接收枚举 ordinal 或 name执行相关的所有枚举
Y1类型字段 添加 @ONodeAttr 注解可接收注解字段对应的值当前类型
Z1类型静态方法 添加 @ONodeCreator 注解可接收注解方法参数对应的值当前类型

演示素材

@Getter
public class User {
    private final String name;
    private final int age;
    private final Gender gender;

    public User(String name, int age, Gender gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
}

@Getter
public enum Gender {
    UNKNOWN(10, "未知的性别"), MALE(11, "男"), FEMALE(12, "女"), UNSTATED(19, "未说明的性别");

    private final int code;
    private final String name;

    Gender(int code, String name) {
        this.code = code;
        this.name = name;
    }
    
    @Override
    public String toString() {
        return name;
    }

    public static Gender fromCode(Integer code) {
        for (Gender gender : Gender.values()) {
            if (gender.code == code) {
                return gender;
            }
        }

        return UNKNOWN;
    }
}

1、编码(序列化) - 方式A:默认

默认输出 ordinal 值

@Test
public void case11() {
    User user = new User("solon", 22, Gender.MALE);
    
    String json = ONode.serialize(user);
    System.out.println(json);
    Assertions.assertEquals("{\"name\":\"solon\",\"age\":22,\"gender\":1}", json);
}

2、编码(序列化) - 方式B:选项特性

选项特性是指:序列化时添加特性,或通过 Options 添加特性,对本次序列化进行控制。

  • 使用 Write_EnumUsingName 特性
@Test
public void case21() {
    User user = new User("solon", 22, Gender.MALE);
    
    String json = ONode.serialize(user, Feature.Write_EnumUsingName);
    System.out.println(json);

    Assertions.assertEquals("{\"name\":\"solon\",\"age\":22,\"gender\":\"MALE\"}", json);
}
  • 使用 Write_EnumUsingToString 特性
@Test
public void case22() {
    User user = new User("solon", 22, Gender.MALE);
    
    String json = ONode.serialize(user, Feature.Write_EnumUsingToString);
    System.out.println(json);

    Assertions.assertEquals("{\"name\":\"solon\",\"age\":22,\"gender\":\"男\"}", json);
}
  • 使用 Write_EnumShapeAsObject 特性(输出枚举的所有字段)
@Test
public void case23() {
    User user = new User("solon", 22, Gender.MALE);
    
    String json = ONode.serialize(user, Feature.Write_EnumShapeAsObject);
    System.out.println(json);

    Assertions.assertEquals("{\"name\":\"solon\",\"age\":22,\"gender\":{\"code\":11,\"name\":\"男\"}}", json);
}

3、编码(序列化) - 方式C:类型特性(v4.0.11 后支持)

类型特性是指,为枚举类型添加 @ONodeAttr 注解,并附加特性。

@ONodeAttr(features=Feature.Write_EnumUsingName)
public static enum Gender {...}

@Test
public void case31() {
    User user = new User("solon", 22, Gender.MALE);
    
    String json = ONode.serialize(user);
    System.out.println(json);

    Assertions.assertEquals("{\"name\":\"solon\",\"age\":22,\"gender\":\"MALE\"}", json);
}

其它特性演示,略过。

4、编码(序列化) - 方式D:类型字段添加注解(优先级第二高)

类型字段添加注解,是指在枚举类型的字段上添加注解,以此字段值代表此类型输出。

public static enum Gender {
    UNKNOWN(10, "未知的性别"), MALE(11, "男"), FEMALE(12, "女"), UNSTATED(19, "未说明的性别");
    
    @ONodeAttr
    private final int code;
    private final String name;

    Gender(int code, String name) {
        this.code = code;
        this.name = name;
    }
}

//本例把 `code` 的值作为此枚举类型输出。

@Test
public void case41() {
    User user = new User("solon", 22, Gender.MALE);
    
    String json = ONode.serialize(user);
    System.out.println(json);
    Assertions.assertEquals("{\"name\":\"solon\",\"age\":22,\"gender\":11}", json);
}

5、编码(序列化) - 方式E:类型自定义编解码(优先级最高)

类型自定义编解码,是指通过 Options 添加特定类型的编解码器(优先级最高)。

@Test
public void case51() {
    User user = new User("solon", 22, Gender.MALE);
    
    //模拟 Feature.Write_EnumShapeAsObject 效果
    Options options = Options.of().addEncoder(Gender.class, (ctx, value, target) -> {
        return target.set("code", value.getCode()).set("name", value.getName());
    });

    String json = ONode.serialize(user, options);
    System.out.println(json);
    Assertions.assertEquals("{\"name\":\"solon\",\"age\":22,\"gender\":{\"code\":11,\"name\":\"男\"}}", json);
}

6、编码(序列化) - 方式F:值字段特性

值字段特性,是指在用到它的字段,通过注解附加特性。

public class User {
    private final String name;
    private final int age;
    @ONodeAttr(features=Feature.Write_EnumUsingName)
    private final Gender gender;

    public User(String name, int age, Gender gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
}

@Test
public void case61() {
    User user = new User("solon", 22, Gender.MALE);

    String json = ONode.serialize(user, options);
    System.out.println(json);
    Assertions.assertEquals("{\"name\":\"solon\",\"age\":22,\"gender\":\"MALE\"}", json);
}

7、解码(反序列化) - 方式X:默认

默认状态下,支持 ordinalname 输入

Assertions.assertEquals(Gender.MALE, ONode.deserialize("1", Gender.class));
Assertions.assertEquals(Gender.MALE, ONode.deserialize("\"MALE\"", Gender.class));

8、解码(反序列化) - 方式Y:类型字段 添加 @ONodeAttr 注解

此方式与 方式D1 对应。

@Getter
public static enum Gender {
    UNKNOWN(10, "未知的性别"), MALE(11, "男"), FEMALE(12, "女"), UNSTATED(19, "未说明的性别");
    
    @ONodeAttr
    private final int code;
    private final String name;

    Gender(int code, String name) {
        this.code = code;
        this.name = name;
    }
}

要求输入值与注解的字段对象

Assertions.assertEquals(Gender2.MALE, ONode.deserialize("11", Gender.class));

9、解码(反序列化) - 方式Z:类型静态方法 添加 @ONodeCreator 注解

使用 @ONodeCreator 时,要求必须是:静态方法,且只有一个参数。

@Getter
public static enum Gender {
    UNKNOWN(10, "未知的性别"), MALE(11, "男"), FEMALE(12, "女"), UNSTATED(19, "未说明的性别");

    private final int code;
    private final String name;

    Gender(int code, String name) {
        this.code = code;
        this.name = name;
    }

    @ONodeCreator
    public static Gender fromCode(Integer code) {
        for (Gender gender : Gender.values()) {
            if (gender.code == code) {
                return gender;
            }
        }

        return UNKNOWN;
    }
}

可以是值(或对象属性)与参数对应上

//值对上
Assertions.assertEquals(Gender2.MALE, ONode.deserialize("11", Gender.class));

//或者对象的同名属性对上
Assertions.assertEquals(Gender2.MALE, ONode.deserialize("{\"code\":11,\"name\":\"男\"}", Gender.class));