Jackson - How to process (deserialize) nested JSON?

后端 未结 3 635
遇见更好的自我
遇见更好的自我 2020-11-29 01:18
{
  vendors: [
    {
      vendor: {
        id: 367,
        name: \"Kuhn-Pollich\",
        company_id: 1,
      }
    },
    {
      vendor: {
        id: 374,
           


        
相关标签:
3条回答
  • 2020-11-29 01:53

    @Patrick I would improve your solution a bit

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

    It works faster :)

    0 讨论(0)
  • 2020-11-29 02:00

    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.

    Given this JSON:

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

    And these model objects:

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

    and

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

    Where the Jackson voodoo is implemented like:

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

    and

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

    Hope this is useful to someone!

    0 讨论(0)
  • 2020-11-29 02:09

    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.

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

    If so then there are two things to consider:

    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.

    So, play around with:

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

    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:

    class VendorWrapper
    {
        Vendor vendor;
    
        // gettors, settors for vendor if you need them
    }
    

    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:

    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); 
    

    The object tree for VendorsWrapper is analogous to your JSON:

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

    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:

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

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

    0 讨论(0)
提交回复
热议问题