【问题标题】:How to serialize/deserialize enum usin Gson如何使用 Gson 序列化/反序列化枚举
【发布时间】:2021-11-28 07:05:50
【问题描述】:

我需要序列化/反序列化一个特定的枚举:

public enum Status {
    ONLINE("online"),
    OFFLINE("offline"),
    UNKNOWN("del") {
        @Override
        public String getStatusOld() {
            return "off";
        }
    };
  
    private final String name;
    Status(String name) {
        this.name = name;
    }

    public String getStatusOld() {
        return name;
    }
    
    public String toString() {
        return name;
    }

    public static Status typeOf(String name) {
        if (!StringUtils.isEmpty(name)) {
            for (Status type : values()) {
                if (type.name.equalsIgnoreCase(name)) {
                    return type;
                }
                if (type.getStatusOld().equalsIgnoreCase(name)) {
                    return type;
                }
            }
        }
        throw new IllegalArgumentException("Unknown status type: " + name);
    }
}

我的 POJO 如下所示:

public class User {
    public String name;
    public Status status;
    ...
}

为了序列化/反序列化我的枚举 Status 我已经创建了自定义序列化器/反序列化器:

public class GsonStatusEnumSerializer implements JsonSerializer<Status> {
    @Override
    public JsonElement serialize(Status status, Type type, JsonSerializationContext jsonSerializationContext) {
        return new JsonPrimitive(status.toString());
    }
}
public class GsonStatusEnumDeserializer implements JsonDeserializer<Status> {
    @Override
    public Status deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        if (jsonElement != null && jsonElement.isJsonPrimitive()) {
            final String status = jsonElement.getAsString();
            if (StringUtils.isNotBlank(status)) {
                return Status.typeOf(status);
            }
        }
        return null;
    }
}

当我尝试序列化我的User 类并且状态具有值UNKNOWN 时,Gson 没有使用我的自定义序列化程序来序列化用户的状态。 我发现它正在发生,因为如果 UNKNOWN 值具有不同的类型。

public static void main(String[] args) {
    for (Status value : Status.values()) {
            System.out.println("class = " + value.getClass() + ", value = " + value);
        }
}

我得到的结果如下所示:

class = class com.test.Status, value = online
class = class com.test.Status$2, value = del
class = class com.test.Status, value = offline

我序列化User POJO的主要方法:

public static void main(String[] args) {
    Gson gson = new GsonBuilder()
                .registerTypeAdapter(Status.class, new GsonStatusEnumDeserializer())
                .registerTypeAdapter(Status.class, new GsonStatusEnumSerializer())
                .create();
    User user = new User();
    user.name = "John";
    user.status = Status.UNKNOWN;
    
    String json = gson.toJson(user);
    User user1 = gson.fromJson(json, User.class);
}

我得到一个例外:

Exception in thread "main" java.lang.IllegalArgumentException: Unknown process type: UNKNOWN
    at com.test.Status.typeOf(Status.java:97)
    at com.test.GsonStatusEnumDeserializer.deserialize(GsonStatusEnumDeserializer.java:22)
    at com.test.GsonStatusEnumDeserializer.deserialize(GsonStatusEnumDeserializer.java:16)

如何通过 Gson 对其进行序列化/反序列化?

【问题讨论】:

  • 这能回答你的问题吗? serialize and deserialize enum with Gson
  • @Aris 这种方法对我不起作用。我的问题是Status.UNKNOWN 的类型。它具有 com.test.Status$2 类型,因此 Gson 忽略了我的自定义序列化程序。
  • 我看到你注册了 2 个序列化程序。尝试删除 GsonProcessTypeEnumDeserializer 看看是否有效
  • @SusanMustafa 我无法删除我的自定义反序列化程序,因为它破坏了我对其他 Status 枚举值的反序列化过程。

标签: java gson


【解决方案1】:

快速修复:如果您确实需要代码工作,请删除 UNKNOWN("del") 后的花括号

我尝试复制您的代码,但我不太了解您的某些决定背后的需求,因此如果这不适用,请提供额外的说明。有几点:

KISS:过度工程 我认为你的很多代码都是不必要的,所以让它保持愚蠢简单。我们可以将状态简化为:

状态枚举:

public enum Status {
    ONLINE,
    OFFLINE,
    UNKNOWN
}

对于 enum 实际在做什么似乎有些混乱。枚举实际上只是为了方便,标识符“ONLINE”、“OFFLINE”和“UNKNOWN”属于 Status 类型。要检查这一点,我们可以这样做:

for (Status value : Status.values()){
    System.out.println(value.getClass().getName());
}

我们得到以下输出:

Status
Status
Status

Process finished with exit code 0

这意味着您不必将其转换为字符串并返回,并且您根本不需要自定义序列化器和反序列化器!

命名

我建议对变量和方法使用一致的命名约定。调用一个类 GsonStatusEnumSerializer 和另一个类 GsonProcessTypeEnumDeserializer (状态与进程)可能很难跟踪,并且会让其他人觉得它们在逻辑上没有联系。

在 Status 中,您有 ONLINE('online')OFFLINE('offline')UNKNOWN('del'),这可能是很多混乱的根源(尤其是当您覆盖 toString() 方法以返回“关闭”时!)

封装 封装是面向对象编程的核心原则,通过 getter 和 setter 访问变量使代码更安全。

最终输出

public static void main(String[] args) {
        Gson gson = new Gson();
        User user = new User("John", Status.UNKNOWN);

        String json = gson.toJson(user);

        System.out.println(json);

        User user1 = gson.fromJson(json, User.class);

        System.out.println(user1.getName());
        System.out.println(user1.getStatus());
    }

运行以上会产生以下输出:

{"name":"John","status":"UNKNOWN"}
John
UNKNOWN

Process finished with exit code 0

祝你所有的编码工作好运:)

【讨论】:

  • @ferrouskid 感谢您采用这种方法。这不是我的代码,由于向后兼容性,我无法更改此枚举。但是你的方法真的很好,我同意你的看法。
【解决方案2】:

我已经找到解决方案了,我得为这个类型单独添加一个序列化器。

public static void main(String[] args) {
    Gson gson = new GsonBuilder()
                .registerTypeAdapter(Status.class, new GsonStatusEnumDeserializer())
                .registerTypeAdapter(Status.class, new GsonStatusEnumSerializer())
                .registerTypeAdapter(Status.UNKNOWN.getClass(), new GsonStatusEnumSerializer())
                .create();
    User user = new User();
    user.name = "John";
    user.status = Status.UNKNOWN;
    
    String json = gson.toJson(user);
    User user1 = gson.fromJson(json, User.class);
}

我不喜欢这种方法,但它对我有用。

【讨论】:

    猜你喜欢
    • 2013-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 2015-11-15
    • 2015-10-19
    • 2013-07-17
    相关资源
    最近更新 更多