Saltar al contenido

Jackson – ¿Cómo procesar (deserializar) JSON anidado?

Pudiera darse el caso de que halles algún problema con tu código o proyecto, recuerda probar siempre en un entorno de testing antes aplicar el código al proyecto final.

Solución:

Aquí hay una solución aproximada pero más declarativa. No he podido reducirlo a una sola anotación, pero esto parece funcionar bien. Tampoco estoy seguro sobre el rendimiento en grandes conjuntos de datos.

Dado este JSON:


    "list": [
        
            "wrapper": 
                "name": "Jack"
            
        ,
        
            "wrapper": 
                "name": "Jane"
            
        
    ]

Y estos objetos modelo:

public class RootObject 
    @JsonProperty("list")
    @JsonDeserialize(contentUsing = SkipWrapperObjectDeserializer.class)
    @SkipWrapperObject("wrapper")
    public InnerObject[] innerObjects;

y

public class InnerObject 
    @JsonProperty("name")
    public String name;

Donde se implementa el vudú de Jackson como:

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface SkipWrapperObject 
    String value();

y

public class SkipWrapperObjectDeserializer extends JsonDeserializer implements
        ContextualDeserializer 
    private Class wrappedType;
    private String wrapperKey;

    public JsonDeserializer createContextual(DeserializationContext ctxt,
            BeanProperty property) throws JsonMappingException 
        SkipWrapperObject skipWrapperObject = property
                .getAnnotation(SkipWrapperObject.class);
        wrapperKey = skipWrapperObject.value();
        JavaType collectionType = property.getType();
        JavaType collectedType = collectionType.containedType(0);
        wrappedType = collectedType.getRawClass();
        return this;
    

    @Override
    public Object deserialize(JsonParser parser, DeserializationContext ctxt)
            throws IOException, JsonProcessingException 
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode objectNode = mapper.readTree(parser);
        JsonNode wrapped = objectNode.get(wrapperKey);
        Object mapped = mapIntoObject(wrapped);
        return mapped;
    

    private Object mapIntoObject(JsonNode node) throws IOException,
            JsonProcessingException 
        JsonParser parser = node.traverse();
        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(parser, wrappedType);
    


¡Espero que sea útil para alguien!

Sus datos son problemáticos porque tiene información interna envoltura objetos en tu array. Presumiblemente tu Vendor objeto está diseñado para manejar id, name, company_idpero cada uno de esos múltiples objetos también está envuelto en un objeto con una sola propiedad vendor.

Supongo que estás usando el Jackson El enlace de datos modelo.

Si es así, hay dos cosas a considerar:

El primero es usar una propiedad de configuración especial de Jackson. Jackson – desde 1.9 creo, esto puede no estar disponible si está usando una versión anterior de Jackson – proporciona UNWRAP_ROOT_VALUE. Está diseñado para casos en los que sus resultados están envueltos en un objeto de propiedad única de nivel superior que desea descartar.

Entonces, juega con:

objectMapper.configure(SerializationConfig.Feature.UNWRAP_ROOT_VALUE, true);

El segundo es usar objetos de envoltura. Incluso después de descartar el objeto de envoltorio externo, todavía tiene el problema de su Vendor objetos que se envuelven en un objeto de propiedad única. Use un envoltorio para evitar esto:

class VendorWrapper

    Vendor vendor;

    // gettors, settors for vendor if you need them

Del mismo modo, en lugar de utilizar UNWRAP_ROOT_VALUES, también podría definir una clase contenedora para manejar el objeto externo. Suponiendo que tienes la correcta Vendor, VendorWrapper objeto, puede definir:

class VendorsWrapper

    List vendors = new ArrayList();

    // gettors, settors for vendors if you need them


// in your deserialization code:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readValue(jsonInput, VendorsWrapper.class); 

El árbol de objetos para VendorsWrapper es análogo a su JSON:

VendorsWrapper:
    vendors:
    [
        VendorWrapper
            vendor: Vendor,
        VendorWrapper:
            vendor: Vendor,
        ...
    ]

Finalmente, podrías usar el Jackson modelo de árbol para analizar esto en JsonNodesdescartando el nodo exterior, y para cada JsonNode en el ArrayNodellamando:

mapper.readValue(node.get("vendor").getTextValue(), Vendor.class);

Eso podría resultar en menos código, pero no parece menos torpe que usar dos contenedores.

@Patrick, mejoraría un poco tu solución

@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException         
    ObjectNode objectNode = jp.readValueAsTree();
    JsonNode wrapped = objectNode.get(wrapperKey);
    JsonParser parser = node.traverse();
    parser.setCodec(jp.getCodec());
    Vendor mapped = parser.readValueAs(Vendor.class);
    return mapped;

Funciona más rápido 🙂

Te mostramos comentarios y valoraciones

Si posees algún reparo y capacidad de acrecentar nuestro escrito te mencionamos escribir una acotación y con placer lo leeremos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Respuestas a preguntas comunes sobre programacion y tecnología