parser objeto leer convertir convert anotaciones java json serialization jackson deserialization

objeto - json parser java jackson



Deserialización de Json en otra jerarquía de clases usando Jackson (3)

Cómo resolví este problema. Aquí hay un diagrama de clase para un proyecto de ejemplo:

Así que quiero obtener el formulario ConcreteA desde ConcreteA después de la deserialización.

Mi solución es extender ClassNameIdResolver para agregar funcionalidad para deserializar objetos de clase base en objetos de clase de subtipo (las clases de subtipo no agregan funcionalidad adicional y campos adicionales).

Aquí hay un código que crea ObjectMapper para la deserialización:

protected ObjectMapper getObjectMapperForDeserialization() { ObjectMapper mapper = new ObjectMapper(); StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY); typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()) { private HashMap<Class, Class> classes = new HashMap<Class, Class>() { { put(ConcreteA.class, ConcreteAAdapter.class); put(ConcreteB.class, ConcreteBAdapter.class); put(ConcreteC.class, ConcreteCAdapter.class); } }; @Override public String idFromValue(Object value) { return (classes.containsKey(value.getClass())) ? value.getClass().getName() : null; } @Override public JavaType typeFromId(String id) { try { return classes.get(Class.forName(id)) == null ? super.typeFromId(id) : _typeFactory.constructSpecializedType(_baseType, classes.get(Class.forName(id))); } catch (ClassNotFoundException e) { // todo catch the e } return super.typeFromId(id); } }); mapper.setDefaultTyping(typeResolverBuilder); return mapper; }

Y aquí hay un código que crea ObjectMapper para la serialización:

protected ObjectMapper getObjectMapperForSerialization() { ObjectMapper mapper = new ObjectMapper(); StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY); typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance())); mapper.setDefaultTyping(typeResolverBuilder); return mapper; }

Código de prueba:

public static void main(String[] args) throws IOException { JacksonUtils JacksonUtils = new JacksonUtilsImpl(); Collection<Base> data = new LinkedBlockingQueue<Base>(); data.add(new ConcreteA()); data.add(new ConcreteB()); data.add(new ConcreteC()); String json = JacksonUtils.marshallIntoString(data); System.out.println(json); Collection<? extends Adapter> adapters = JacksonUtils.unmarshall(json, new TypeReference<ArrayList<Adapter>>() {}); for (Adapter adapter : adapters) { System.out.println(adapter.getClass().getName()); } }

Código completo de la clase JacksonUtils:

public class JacksonUtilsImpl implements JacksonUtils { @Override public byte[] marshall(Collection<Base> data) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream() { @Override public byte[] toByteArray() { return buf; } }; getObjectMapperForSerialization().writerWithType(new TypeReference<Collection<Base>>() {}).writeValue(out, data); return out.toByteArray(); } @Override public String marshallIntoString(Collection<Base> data) throws IOException { return getObjectMapperForSerialization().writeValueAsString(data); } protected ObjectMapper getObjectMapperForSerialization() { ObjectMapper mapper = new ObjectMapper(); StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY); typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance())); mapper.setDefaultTyping(typeResolverBuilder); return mapper; } protected ObjectMapper getObjectMapperForDeserialization() { ObjectMapper mapper = new ObjectMapper(); StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY); typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()) { private HashMap<Class, Class> classes = new HashMap<Class, Class>() { { put(ConcreteA.class, ConcreteAAdapter.class); put(ConcreteB.class, ConcreteBAdapter.class); put(ConcreteC.class, ConcreteCAdapter.class); } }; @Override public String idFromValue(Object value) { return (classes.containsKey(value.getClass())) ? value.getClass().getName() : null; } @Override public JavaType typeFromId(String id) { try { return classes.get(Class.forName(id)) == null ? super.typeFromId(id) : _typeFactory.constructSpecializedType(_baseType, classes.get(Class.forName(id))); } catch (ClassNotFoundException e) { // todo catch the e } return super.typeFromId(id); } }); mapper.setDefaultTyping(typeResolverBuilder); return mapper; } @Override public Object unmarshall(byte[] json) throws IOException { return getObjectMapperForDeserialization().readValue(json, Object.class); } @Override public <T> T unmarshall(InputStream source, TypeReference<T> typeReference) throws IOException { return getObjectMapperForDeserialization().readValue(source, typeReference); } @Override public <T> T unmarshall(byte[] json, TypeReference<T> typeReference) throws IOException { return getObjectMapperForDeserialization().readValue(json, typeReference); } @Override public <T> Collection<? extends T> unmarshall(String json, Class<? extends Collection<? extends T>> klass) throws IOException { return getObjectMapperForDeserialization().readValue(json, klass); } @Override public <T> Collection<? extends T> unmarshall(String json, TypeReference typeReference) throws IOException { return getObjectMapperForDeserialization().readValue(json, typeReference); } }

Ahora estoy trabajando con Jackson y tengo algunas preguntas al respecto.

Ante todo. Tengo dos servicios, el primero es el servicio de recopilación y envío de datos y el segundo lo recibo y, por ejemplo, lo registro en un archivo.

Entonces, el primer servicio tiene una jerarquía de clases como esta:

+----ConcreteC | Base ----+----ConcreteA | +----ConcreteB

Y el segundo servicio tiene una jerarquía de clases como esta:

ConcreteAAdapter extends ConcreteA implements Adapter {} ConcreteBAdapter extends ConcreteB implements Adapter {} ConcreteCAdapter extends ConcreteC implements Adapter {}

El primer servicio no sabe nada sobre ConcreteXAdapter .

La forma en que estoy enviando los datos en el primer servicio:

Collection<Base> data = new LinkedBlockingQueue<Base>() JacksonUtils utils = new JacksonUtils(); data.add(new ConcreteA()); data.add(new ConcreteB()); data.add(new ConcreteC()); ... send(utils.marshall(data)); ... public class JacksonUtils { public byte[] marshall(Collection<Base> data) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream() { @Override public byte[] toByteArray() { return buf; } }; getObjectMapper().writeValue(out, data); return out.toByteArray(); } protected ObjectMapper getObjectMapper() { return new ObjectMapper(); } public Object unmarshall(byte[] json) throws IOException { return getObjectMapper().readValue(json, Object.class); } public <T> T unmarshall(InputStream source, TypeReference<T> typeReference) throws IOException { return getObjectMapper().readValue(source, typeReference); } public <T> T unmarshall(byte[] json, TypeReference<T> typeReference) throws IOException { return getObjectMapper().readValue(json, typeReference); } }

Entonces, quiero desirializar json en la Colección de ConcreteXAdapter , no en la Colección de ConcreteX ( ConcreteA -> ConcreteAAdapter, ConcreteB -> ConcreteBAdapter, ConcreteC -> ConcreteCAdapter ). En el caso que describí quiero obtener:

Collection [ConcreteAAdapter, ConcreteBAdapter, ConcreteCAdapter]

¿Cómo puedo hacer esto?


Me parece que el enfoque de programmerbruce es el más claro y fácil de trabajar (ejemplo a continuación). Obtuve la información de su respuesta a una pregunta relacionada: https://.com/a/6339600/1148030 y la publicación del blog relacionado: http://programmerbruce.blogspot.fi/2011/05/deserialize-json-with-jackson-into.html

También puedes ver esta página wiki amigable (también mencionada en la respuesta de Eugene Retunsky): http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

Otra bonita página wiki: http://wiki.fasterxml.com/JacksonMixInAnnotations

Aquí hay un breve ejemplo para darle la idea:

Configure el ObjectMapper de esta manera:

mapper.getDeserializationConfig().addMixInAnnotations(Base.class, BaseMixin.class); mapper.getSerializationConfig().addMixInAnnotations(Base.class, BaseMixin.class);

Ejemplo de clase BaseMixin (fácil de definir como una clase interna).

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type") @JsonSubTypes({ @JsonSubTypes.Type(value=ConcreteA.class, name="ConcreteA"), @JsonSubTypes.Type(value=ConcreteB.class, name="ConcreteB") }) private static class BaseMixin { }

En el segundo servicio podría definir la BaseMixin así:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type") @JsonSubTypes({ @JsonSubTypes.Type(value=ConcreteAAdapter.class, name="ConcreteA"), @JsonSubTypes.Type(value=ConcreteBAdapter.class, name="ConcreteB") }) private static class BaseMixin { }


Para este propósito necesitas pasar información adicional en JSON:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="@type") class Base { ... }

Luego en la serialización agregará el campo @type:

objectMapper.registerSubtypes( new NamedType(ConcreteAAdapter.class, "ConcreteA"), new NamedType(ConcreteBAdapter.class, "ConcreteB"), new NamedType(ConcreteCAdapter.class, "ConcreteC") ); // note, that for lists you need to pass TypeReference explicitly objectMapper.writerWithType(new TypeReference<List<Base>>() {}) .writeValueAsString(someList); { "@type" : "ConcreteA", ... }

en deserialización será:

objectMapper.registerSubtypes( new NamedType(ConcreteA.class, "ConcreteA"), new NamedType(ConcreteB.class, "ConcreteB"), new NamedType(ConcreteC.class, "ConcreteC") ); objectMapper.readValue(....)

Más aquí: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization