Serialize/Deserialize a POJO contain a speciel Enum (not Enum of String) with GSON

时光毁灭记忆、已成空白 提交于 2019-12-11 18:56:26

问题


I need Serialize/Deserialize a POJO contain a speciel Enum (not Enum of String). I find lots of sample with Enum of String but is not my case.

I read Gson docs, and I have a start of solution with implements JsonDeserializer<T>, JsonSerializer<T>

public class ApplicationError {

    private static final long serialVersionUID = 1L;

    private final ErrorCode code;

    private final String description;

    private final URL infoURL;

    ....
}

public enum ErrorCode {
    INVALID_URL_PARAMETER(HttpStatus.BAD_REQUEST, 20, "Invalid URL parameter value"),
    MISSING_BODY(HttpStatus.BAD_REQUEST, 21, "Missing body"),
    INVALID_BODY(HttpStatus.BAD_REQUEST, 22, "Invalid body")
}

public class ErrorCodeDeserializer implements JsonDeserializer<ErrorCode> /*, JsonSerializer<ErrorCode> */{

    @Override
    public ErrorCode deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        ErrorCode[] scopes = ErrorCode.values();
        for (ErrorCode scope : scopes) {
            System.out.println("--------->" + scope + "   " + json.getAsString());
            if (scope.equals(json.getAsString())) {
                return scope;
            }
        }
        return null;
    }

    /*
    @Override
    public JsonElement serialize(ErrorCode arg0, Type arg1, JsonSerializationContext arg2) {
        ???
    }*/
}

...
ApplicationError applicationError = new ApplicationError(ErrorCode.INVALID_URL_PARAMETER,
                    "Application identifier is missing");
....
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(ErrorCode.class, new ErrorCodeDeserializer());
Gson gson = gsonBuilder.create();
gson.toJson(applicationError)

My result is:

{"code":"INVALID_URL_PARAMETER","description":"Application identifier is missing"}

instead of:

{"code":"20", "message":"Invalid URL parameter value", "description":"Application identifier is missing"}

EDIT 1

I try with:

@Override
public JsonElement serialize(ErrorCode src, Type typeOfSrc, JsonSerializationContext context) {
    JsonArray jsonMerchant = new JsonArray();
    jsonMerchant.add("" + src.getCode());
    jsonMerchant.add("" + src.getMessage());
    return jsonMerchant;
}

but my result is:

{"code":["20","Invalid URL parameter value"],"description":"Application identifier is missing"}

EDIT 2

I try with:

@Override
public JsonElement serialize(ErrorCode src, Type typeOfSrc, JsonSerializationContext context) {
    JsonObject result = new JsonObject();
    result.add("code", new JsonPrimitive(src.getCode()));
    result.add("message", new JsonPrimitive(src.getMessage()));
    return result;
}

but my result is:

{"code":{"code":20,"message":"Invalid URL parameter value"},"description":"Application identifier is missing"}

now I want juste change "code":{"code":20,"message":"Invalid URL parameter value"} by "code":20,"message":"Invalid URL parameter value"


回答1:


In general, this is a bad idea for several reasons:

  • Your deserializer (if you need it) needs to be sophisticated if reading the flat properties in streaming mode (making sure the order of properties remains unchanged).
  • Otherwise you need to write a special type adapter for every class using the ErrorCode enum, and you need a custom JsonSerializer/JsonDeserializer for each of them.
  • Deserializing ErrorCode makes little sense to me at all.
  • Gson does not allow "flattening" objects into each other.

In its simplest implementation, I would say that you might want to use something like this:

final class FlatErrorCodeTypeAdapter
        extends TypeAdapter<ErrorCode> {

    private FlatErrorCodeTypeAdapter() {
    }

    @Override
    public void write(final JsonWriter out, final ErrorCode errorCode)
            throws IOException {
        // very bad idea - the serializer may be in a bad state and we assume the host object is being written
        out.value(errorCode.code);
        out.name("message");
        out.value(errorCode.message);
    }

    @Override
    public ErrorCode read(final JsonReader in)
            throws IOException {
        // now fighting with the bad idea being very fragile assuming that:
        // * the code field appears the very first property value
        // * we ignore the trailing properties and pray the host object does not have "message" itself
        // * no matter what "message" is -- it simply does not have sense
        final int code = in.nextInt();
        return ErrorCode.valueByCode(code);
    }

}

Then in your code something like this:

final class ApplicationError {

    @JsonAdapter(FlatErrorCodeTypeAdapter.class)
    final ErrorCode code;
    final String description;

    ApplicationError(final ErrorCode code, final String description) {
        this.code = code;
        this.description = description;
    }

}

Example of use:

private static final Gson gson = new Gson();

...

final ApplicationError before = new ApplicationError(ErrorCode.INVALID_URL_PARAMETER, "Application identifier is missing");
final String json = gson.toJson(before);
System.out.println(json);
final ApplicationError after = gson.fromJson(json, ApplicationError.class);
System.out.println(before.code == after.code);
System.out.println(before.description.equals(after.description));

Output:

{"code":20,"message":"Invalid URL parameter value","description":"Application identifier is missing"}
true
true

I still assume this a very fragile solution and I would simply recommend you to redesign your ApplicationError and "flatten" ErrorCode yourself:

final class ApplicationError {

    final int code;
    final String message;
    final String description;

    ApplicationError(final ErrorCode errorCode, final String description) {
        this.code = errorCode.code;
        this.message = errorCode.message;
        this.description = description;
    }

    ...

    final ErrorCode resolveErrorCode() {
        final ErrorCode errorCode = ErrorCode.valueByCode(code);
        if ( !errorCode.message.equals(message) ) {
            throw new AssertionError('wow...');
        }
        return errorCode;
    }

}

With the latter you don't even need your Gson to be configured in any way.



来源:https://stackoverflow.com/questions/50599504/serialize-deserialize-a-pojo-contain-a-speciel-enum-not-enum-of-string-with-gs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!