Home > front end >  How to serialize/deserialize enum usin Gson
How to serialize/deserialize enum usin Gson

Time:10-08

I need to serialize/deserialize a specific enum:

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);
    }
}

and I have the POJO looks like the following:

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

and order to serialize/deserialize my enum Status I have created the custom serializer/deserializer:

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;
    }
}

and when I'm trying to serialize my User class and the Status has value UNKNOWN my custom serializer isn't using by Gson for serializing User's status. I've found it is happening because in case UNKNOWN value has a different type.

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

and I got the results looks like the following:

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

My main method for serializing the 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);
}

and I get an exception:

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)

How can I serialize/deserialize it by Gson?

CodePudding user response:

I have found the solution, I have to add a serializer for this type separately.

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);
}

I don't like this approach, but it works for me.

CodePudding user response:

Quick fix: if you really need your code to work, remove the curly braces after UNKNOWN("del")

I tried replicating your code, but I don't quite understand the need behind some of your decisions, so please to provide additional clarification if this is not applicable. There are a few points:

KISS: Over engineering I think a lot of your code is unnecessary, so lets Keep It Stupid Simple. We can simplify Status to be:

Status enum:

public enum Status {
    ONLINE,
    OFFLINE,
    UNKNOWN
}

It appears there is some confusion about what enum is actually doing. Enum is just for convenience really, the identifiers "ONLINE", "OFFLINE", and "UNKNOWN" are of the type Status. To check this we can do:

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

And we get the following output:

Status
Status
Status

Process finished with exit code 0

What this means is that you don't have to convert it to strings and back, and that you don't need your custom serializer and deserializer at all!

Naming

I'd suggest having a consistent naming convention for you variables and methods. Calling one class GsonStatusEnumSerializer and another GsonProcessTypeEnumDeserializer (status vs process) can be tricky to keep track of, and makes it appear to others that they're not logically connected.

In Status, you had ONLINE('online') and OFFLINE('offline') but UNKNOWN('del'), which can be the root of a lot of confusion (especially when you're overriding the toString() method to return "off"!)

Encapsulation Encapsulation is a core principle of object oriented programming, making variables accessible via getters and setters makes code safer.

Final output

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());
    }

Running the above produces the following output:

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

Process finished with exit code 0

Good luck with all your coding endeavours :)

  • Related