Rectangle 27 0

java Jackson How to process (deserialize) nested JSON?


@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface SkipWrapperObject {
    String value();
}
JsonNode
ObjectMapper.convertValue()
mapIntoObject
mapIntoObject(JsonNode)
public class InnerObject {
    @JsonProperty("name")
    public String name;
}
public class RootObject {
    @JsonProperty("list")
    @JsonDeserialize(contentUsing = SkipWrapperObjectDeserializer.class)
    @SkipWrapperObject("wrapper")
    public InnerObject[] innerObjects;
}
public class SkipWrapperObjectDeserializer extends JsonDeserializer<Object> 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);
    }
}
readValue
return mapper.convertValue(node, wrappedType);
traverse()
{
    "list": [
        {
            "wrapper": {
                "name": "Jack"
            }
        },
        {
            "wrapper": {
                "name": "Jane"
            }
        }
    ]
}

Here is a rough but more declarative solution. I haven't been able to get it down to a single annotation, but this seems to work well. Also not sure about performance on large data sets.

Where the Jackson voodoo is implemented like:

and

Note
Rectangle 27 0

java Jackson How to process (deserialize) nested JSON?


@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;
}

@Patrick I would improve your solution a bit

Note
Rectangle 27 0

java Jackson How to process (deserialize) nested JSON?


VendorsWrapper:
    vendors:
    [
        VendorWrapper
            vendor: Vendor,
        VendorWrapper:
            vendor: Vendor,
        ...
    ]
class VendorWrapper
{
    Vendor vendor;

    // gettors, settors for vendor if you need them
}
class VendorsWrapper
{
    List<VendorWrapper> vendors = new ArrayList<VendorWrapper>();

    // gettors, settors for vendors if you need them
}

// in your deserialization code:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readValue(jsonInput, VendorsWrapper.class);
mapper.readValue(node.get("vendor").getTextValue(), Vendor.class);
objectMapper.configure(SerializationConfig.Feature.UNWRAP_ROOT_VALUE, true);

Finally, you might use the Jackson Tree Model to parse this into JsonNodes, discarding the outer node, and for each JsonNode in the ArrayNode, calling:

I'd also like to see a solution using something like UNWRAP_ROOT_VALUES, only 1 level deeper, but I don't think it's possible. Another option of course is to use a custom deserializer, and just add hooks to look for the actual objects you're interested in and discard everything else, or to use the Jackson Tree Model approach, throw away the top-level JsonNode, and take the JsonNodes that wrap your Vendors, call getTextValue, and pass that to mapper.readValue. Jackson really gives you a surfeit of options.

I'm assuming that you're using the Jackson Data Binding model.

Similarly, instead of using UNWRAP_ROOT_VALUES, you could also define a wrapper class to handle the outer object. Assuming that you have correct Vendor, VendorWrapper object, you can define:

Thank you, this is how I was doing it I just hoped there was a better way. I'll mark it as correct.

That might result in less code, but it seems no less clumsy than using two wrappers.

The first is using a special Jackson config property. Jackson - since 1.9 I believe, this may not be available if you're using an old version of Jackson - provides UNWRAP_ROOT_VALUE. It's designed for cases where your results are wrapped in a top-level single-property object that you want to discard.

The second is using wrapper objects. Even after discarding the outer wrapper object you still have the problem of your Vendor objects being wrapped in a single-property object. Use a wrapper to get around this:

Your data is problematic in that you have inner wrapper objects in your array. Presumably your Vendor object is designed to handle id, name, company_id, but each of those multiple objects are also wrapped in an object with a single property vendor.

Note