Deserialization of java.utils.logging.Level class with Jackson returns null

天大地大妈咪最大 提交于 2020-02-04 22:18:49

问题


I am trying to deserialize a complex bean using jackson and Lombok builder. I have solved already many errors related to serialization of other custom types but I am stuck with deserializing a Level object. The serialization works somehow but not the deserialization as shown below.

Since the Level class is protected, I cannot for instance create an auxiliary variable to serialize the Level object attributes in a different way (e.g. an array of strings) and then call the constructor of Level after reading the serialized auxiliary variable like I did when deserializing a Pair type.

The bean I am trying to deserialize is much more complex than the following, but I was able to reproduce the problem in a simpler more understandable case study.

TL;dr

Deserialization of java.util.logging.Level with jackson is always null

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

import java.io.IOException;
import java.util.logging.Level;

public class ReproduceSerializationError {

public static void main(String[] args) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    MyObj obj1 = MyObj.builder()
                .level(Level.FINE)
                .var1(10.0)
                .build();
    System.out.println("initial object = " + obj1.toString());

    String serializedOj = serializeObj( obj1, objectMapper );
    System.out.println("serialized json = " + serializedOj.toString());

    MyObj deserialized = deserializeObj(serializedOj, objectMapper);
    System.out.println("deserialized object = " + deserialized.toString());
    System.out.println("logging level of deserialized object = " + deserialized.loggingLevel);
}

@ToString
@Getter
@JsonTypeInfo(use= JsonTypeInfo.Id.CLASS, property="class")
@JsonDeserialize(builder = MyObj.MyObjBuilder.class)
private static class MyObj{

    private final double var1;
    private Level level;

    @JsonPOJOBuilder(withPrefix = "")
    public static class MyObjBuilder {

    }

    @Builder
    public MyObj(Level level, double var1){
        this.level = level;
        this.var1 = var1;
    }
}

public static String serializeObj(MyObj obj1, ObjectMapper objectMapper) {
    try {
        return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj1);
    } catch(JsonProcessingException e) {
        throw new RuntimeException( e );
    }
}

public static MyObj deserializeObj(String jsonInFile, ObjectMapper objectMapper) throws IOException {
    return objectMapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).readValue( jsonInFile, MyObj.class);
    }
}

The code above prints the following:

initial object = ReproduceSerializationError.MyObj(var1=10.0, loggingLevel=FINE)
serialized json = {
  "class" : "org.targomo.gp2.util.ReproduceSerializationError$MyObj",
  "var1" : 10.0,
  "loggingLevel" : {
    "name" : "FINE",
    "resourceBundleName" : "sun.util.logging.resources.logging",
    "localizedName" : "FINE"
  }
}
deserialized object = ReproduceSerializationError.MyObj(var1=10.0, loggingLevel=null)
logging level of deserialized object = null

How can I correctly deserialize the loggingLevel attribute of MyObj?


回答1:


You need to implement custom serialiser and deserialiser for java.util.logging.Level class. Since levels are predefined we can not create them, we should use already created and declared instances. See below implementation:

class LevelJsonSerializer extends JsonSerializer<Level> {

    @Override
    public void serialize(Level value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(value.getName());
    }
}

class LevelJsonDeserializer extends JsonDeserializer<Level> {

    @Override
    public Level deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String name = p.getText();

        return Level.parse(name);
    }
}

You can register above classes like below:

SimpleModule levelModule = new SimpleModule("LevelModule");
levelModule.addSerializer(Level.class, new LevelJsonSerializer());
levelModule.addDeserializer(Level.class, new LevelJsonDeserializer());

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(levelModule);


来源:https://stackoverflow.com/questions/59578292/deserialization-of-java-utils-logging-level-class-with-jackson-returns-null

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