name - java spring jackson
Serializar/Deserializar Mapa<Cadena, Objeto> con Jackson (1)
Tengo una clase que se parece a la siguiente
public class MyClass {
private String val1;
private String val2;
private Map<String,Object> context;
// Appropriate accessors removed for brevity.
...
}
Estoy buscando poder hacer el viaje de ida y vuelta con Jackson de objeto a JSON y viceversa. Puedo serializar el objeto arriba bien y recibir el siguiente resultado:
{
"val1": "foo",
"val2": "bar",
"context": {
"key1": "enumValue1",
"key2": "stringValue1",
"key3": 3.0
}
}
El problema que estoy encontrando es que dado que los valores en el mapa serializado no tienen información de tipo, no se deserializan correctamente. Por ejemplo, en el ejemplo anterior, enumValue1 se debe deserializar como un valor de enumeración, pero en su lugar se deserializa como una cadena. He visto ejemplos para basar qué tipo en una variedad de cosas, pero en mi caso, no sabré cuáles son los tipos (serán objetos generados por el usuario que no sabré de antemano), así que necesito ser capaz de serializar la información de tipo con el par de valores clave. ¿Cómo puedo lograr esto con Jackson?
Para el registro, estoy usando la versión 2.4.2 de Jackson. El código que estoy usando para probar el viaje de ida y vuelta es el siguiente:
@Test
@SuppressWarnings("unchecked")
public void testJsonSerialization() throws Exception {
// Get test object to serialize
T serializationValue = getSerializationValue();
// Serialize test object
String json = mapper.writeValueAsString(serializationValue);
// Test that object was serialized as expected
assertJson(json);
// Deserialize to complete round trip
T roundTrip = (T) mapper.readValue(json, serializationValue.getClass());
// Validate that the deserialized object matches the original one
assertObject(roundTrip);
}
Dado que este es un proyecto basado en Spring, el asignador se crea de la siguiente manera:
@Configuration
public static class SerializationConfiguration {
@Bean
public ObjectMapper mapper() {
Map<Class<?>, Class<?>> mixins = new HashMap<Class<?>, Class<?>>();
// Add unrelated MixIns
..
return new Jackson2ObjectMapperBuilder()
.featuresToDisable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)
.dateFormat(new ISO8601DateFormatWithMilliSeconds())
.mixIns(mixins)
.build();
}
}
Creo que la forma más sencilla de lograr lo que quieres es usando:
ObjectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
Esto agregará información de tipo en el json serializado.
Aquí tienes un ejemplo de ejecución, que necesitarás adaptar a Spring:
public class Main {
public enum MyEnum {
enumValue1
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
MyClass obj = new MyClass();
obj.setContext(new HashMap<String, Object>());
obj.setVal1("foo");
obj.setVal2("var");
obj.getContext().put("key1", "stringValue1");
obj.getContext().put("key2", MyEnum.enumValue1);
obj.getContext().put("key3", 3.0);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(json);
MyClass readValue = mapper.readValue(json, MyClass.class);
//Check the enum value was correctly deserialized
Assert.assertEquals(readValue.getContext().get("key2"), MyEnum.enumValue1);
}
}
El objeto será serializado en algo similar a:
[ "so_27871226.MyClass", {
"val1" : "foo",
"val2" : "var",
"context" : [ "java.util.HashMap", {
"key3" : 3.0,
"key2" : [ "so_27871226.Main$MyEnum", "enumValue1" ],
"key1" : "stringValue1"
} ]
} ]
Y será deserializado de nuevo correctamente, y la afirmación pasará.
Para más información, hay más formas de hacerlo, visite https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization para obtener más información.
Espero que te ayude.