枚举（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` 注解              | 可接收注解方法参数对应的值     |  当前类型     |




### 演示素材

```java
@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 值

```java
@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 特性

```java
@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 特性

```java
@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 特性（输出枚举的所有字段）

```java
@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` 注解，并附加特性。

```java
@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：类型字段添加注解（优先级第二高）


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

```java
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 添加特定类型的编解码器（优先级最高）。

```java
@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：值字段特性

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

```java
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：默认

默认状态下，支持 `ordinal` 或 `name` 输入

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

### 8、解码（反序列化） - 方式Y：类型字段 添加 `@ONodeAttr` 注解

此方式与 `方式D1` 对应。

```java
@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;
    }
}
```

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

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

### 9、解码（反序列化） - 方式Z：类型静态方法 添加 @ONodeCreator 注解

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

```java
@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;
    }
}
```

可以是值（或对象属性）与参数对应上

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

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