简体   繁体   中英

custom jackson serializer not working with gson

I'm making a web app using Jackson & Jersey. I've made Response POJOs for the APIs hosted. I've made a custom Jackson serializer for a date field as below.

class A{
    LocalDateTime localDateTime = LocalDateTime.now();

    @JsonSerialize(using = JacksonSerializers.DateSecondsFromEpochUTCSerializer.class)
    public LocalDateTime getLocalDateTime() {
        return localDateTime;
    }

    public void setLocalDateTime(LocalDateTime localDateTime) {
        this.localDateTime = localDateTime;
    }
}

And the Serializer is as below:

public class JacksonSerializers {

@Component
public static class DateSecondsFromEpochUTCSerializer extends JsonSerializer<LocalDateTime> {

    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator gen, SerializerProvider provider)
            throws IOException, JsonProcessingException {
        gen.writeNumber(localDateTime.toEpochSecond(ZoneOffset.UTC));
    }

}

The above code is working fine when the API is called and the response contains the date in the epoch format.

However, The above serialization is not working when I'm trying to make a JSON of the class using GSON lib. I'm getting the below incorrect output:

System.out.print(new Gson().toJson(new A()));

gives

{"localDateTime":{"date":{"year":2017,"month":1,"day":31},"time":{"hour":12,"minute":55,"second":53,"nano":481000000}}}

But I want 1485847871 . Basically, I want to serialize LocalDateTime in epoch time in both flows. I'm trying to do now with GSON but it is not a hardcore requirement. Any elegant solution to achieve both results would do.

It's possible to create a unified serializer that you can use for both Jackson and GSON. Something along the lines of this:

public class DateSecondsFromEpochUTCSerializer
        extends com.fasterxml.jackson.databind.JsonSerializer<LocalDateTime>
        implements com.google.gson.JsonSerializer<LocalDateTime> {

    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeNumber(convert(localDateTime));
    }

    @Override
    public JsonElement serialize(LocalDateTime ldt, Type type, JsonSerializationContext jsonSerializationContext) {
        return new JsonPrimitive(convert(ldt));
    }

    private long convert(LocalDateTime ldt) {
        return ldt.toEpochSecond(ZoneOffset.UTC);
    }
}

Install it in your GSON instance like this:

Gson gson = new GsonBuilder()
    .registerTypeAdapter(LocalDateTime.class, new DateSecondsFromEpochUTCSerializer())
    .create();
System.out.println(gson.toJson(LocalDateTime.now())); // Epoch

If you're still fine with Gson, and not require Jackson to be introduced since it's a totally different library, you can use a custom Gson type adapter:

final class LocalDateTimeTypeAdapter
        extends TypeAdapter<LocalDateTime> {

    private static final TypeAdapter<LocalDateTime> localDateTimeTypeAdapter = new LocalDateTimeTypeAdapter();

    private LocalDateTimeTypeAdapter() {
    }

    static TypeAdapter<LocalDateTime> getLocalDateTimeTypeAdapter() {
        return localDateTimeTypeAdapter;
    }

    @Override
    @SuppressWarnings("resource")
    public void write(final JsonWriter out, final LocalDateTime value)
            throws IOException {
        out.value(value.toEpochSecond(UTC));
    }

    @Override
    public LocalDateTime read(final JsonReader in)
            throws IOException {
        return Instant.ofEpochSecond(in.nextLong()).atZone(UTC).toLocalDateTime();
    }

}

This is similar to Gson the JsonSerializer / JsonDeserializer pair, but it's more efficient since the latter require JSON trees to be built in memory, whilst type adapters work in streaming fashion and may generate values not accumulating an intermediate state much. Then just configure your Gson instance:

final Gson gson = new GsonBuilder()
        .registerTypeAdapter(LocalDateTime.class, getLocalDateTimeTypeAdapter())
        .create();
final String json = gson.toJson(new A());
out.println(json);
final A a = gson.fromJson(json, A.class);
out.println(a.localDateTime);

And it would produce something like:

{"localDateTime":1485871467}
2017-01-31T14:04:27

both for serialization and deserialization.

对我有用的解决方案:

new ObjectMapper().writeValueAsString(new A())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM