问题
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