Need MixIn Resolution for non-static Inner Class using ObjectMapper - Java 6

喜欢而已 提交于 2021-02-11 06:43:59

问题


I'm facing an issue while using ObjectMapper for non-static inner class. I need to create MixIn to make it work but could not reach to the solution. Below is my class(which I can't change) and the MixIn, I tried. Help needed to create such MixIn.

============================

Base Class

    public class NestedClass implements Serializable{

    private static final long serialVersionUID = -4509619645418618657L;

    private NestedInnerClass innerClass;

    public NestedClass() {
        innerClass = null;
        setInnerClass(new NestedInnerClass(new NestedInnerClass2(), new NestedInnerClass3()));
    }

    public NestedClass(NestedClass nestedCls) {
        innerClass = null;
        setInnerClass(nestedCls.getInnerClass());
    }

    public class NestedInnerClass implements Serializable{
        private static final long serialVersionUID = 9099474732768960830L;
        NestedClass.NestedInnerClass2 nestedInnerClass2;
        NestedClass.NestedInnerClass3 nestedInnerClass3;

        public NestedInnerClass() { 
            super();
        }

        public NestedInnerClass(NestedInnerClass2 nestedInnerClass2, NestedInnerClass3 nestedInnerClass3) {
            super();
            this.nestedInnerClass2 = nestedInnerClass2;
            this.nestedInnerClass3 = nestedInnerClass3;
        }

        public NestedClass.NestedInnerClass2 getNestedInnerClass2() {
            return nestedInnerClass2;
        }

        public void setNestedInnerClass2(NestedClass.NestedInnerClass2 nestedInnerClass2) {
            this.nestedInnerClass2 = nestedInnerClass2;
        }

        public NestedClass.NestedInnerClass3 getNestedInnerClass3() {
            return nestedInnerClass3;
        }

        public void setNestedInnerClass3(NestedClass.NestedInnerClass3 nestedInnerClass3) {
            this.nestedInnerClass3 = nestedInnerClass3;
        }

    }

    public class NestedInnerClass2 implements Serializable{

        private static final long serialVersionUID = -3451502802923307744L;
        String nestedString;
        HashMap<String, String> nestedHashMap = new HashMap<String, String>();

        public NestedInnerClass2() {
            super();
        }

        public NestedInnerClass2(String nestedString, HashMap<String, String> nestedHashMap) {
            super();
            this.nestedString = nestedString;
            this.nestedHashMap = nestedHashMap;
        }

        public NestedInnerClass2(String nestedString) {
            this.nestedString = nestedString;
        }

        public String getNestedString() {
            return nestedString;
        }

        public void setNestedString(String nestedString) {
            this.nestedString = nestedString;
        }

        public HashMap<String, String> getNestedHashMap() {
            return nestedHashMap;
        }

        public void setNestedHashMap(HashMap<String, String> nestedHashMap) {
            this.nestedHashMap = nestedHashMap;
        }

    }

    public class NestedInnerClass3 implements Serializable{

        private static final long serialVersionUID = 1799737022784300052L;
        String nestedString;

        public NestedInnerClass3() {
            super();
        }

        public NestedInnerClass3(String nestedString) {
            super();
            this.nestedString = nestedString;
        }

        public String getNestedString() {
            return nestedString;
        }

        public void setNestedString(String nestedString) {
            this.nestedString = nestedString;
        }

    }

    public NestedInnerClass getInnerClass() {
        return innerClass;
    }

    public void setInnerClass(NestedInnerClass innerClass) {
        this.innerClass = innerClass;
    }


}

=================================

Child Class of Nested Class :

public class NestedClassChild extends NestedClass implements Serializable, Cloneable{

    private static final long serialVersionUID = 7022339501842754692L;

    public NestedClassChild() {}
}

=================================

Assist Class :

public class NestedClassAssist {

    public static void setNestedValues(NestedClass nestedClass, String key, String value, String nestedString)
    {
        if(nestedClass != null && nestedClass.getInnerClass() != null && nestedClass.getInnerClass().getNestedInnerClass2() != null)
        {
            HashMap<String, String> hashMap = new HashMap<String, String>();
            hashMap.put(key, value);
            nestedClass.getInnerClass().getNestedInnerClass2().setNestedHashMap(hashMap);
            nestedClass.getInnerClass().getNestedInnerClass2().setNestedString(nestedString);
        }
    }

     public static void setValue(NestedClass nestedClass, String value){
         setNestedValues(nestedClass, "keyStr", value, "ABC");
     }
}

=================================

To convert to JSON payload :

public class NestedClassToJson {

    public static void main(String[] args) {
        NestedClassChild nestedClassChild = new NestedClassChild();
        NestedClassAssist.setValue(nestedClassChild, "12345");

        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.writerWithDefaultPrettyPrinter();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        try {
            mapper.writeValue(new File("json/testNested.json"),nestedClassChild);
        } catch (Exception e) {  
            e.printStackTrace();
        }
    }
}

=================================

Generated JSON payload from above class :

{
  "innerClass" : {
    "nestedInnerClass2" : {
      "nestedString" : "ABC",
      "nestedHashMap" : {
        "keyStr" : "12345"
      }
    },
    "nestedInnerClass3" : {
      "nestedString" : null
    }
  }
}

=================================

Class to de-serialize from JSON :

public class NestedClassFromJson {

    public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.writerWithDefaultPrettyPrinter();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        NestedClass objectNested = mapper.readValue(getPostBodyAsJSON(), NestedClassChild.class);
        System.out.println(mapper.writeValueAsString(objectNested));
    }

    private static String getPostBodyAsJSON() {
        StringBuffer postBody = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = new BufferedReader(new FileReader(new File("json/testNested.json")));
            while ((line = reader.readLine()) != null)
                postBody.append(line);
        } catch (IOException e) {
            throw new RuntimeException("Issue Occured While Reading POST Body", e);
        }

        return postBody.toString();
    }
}

=================================

But I'm getting below Exception (though I do have default contructor) :

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.test.jackson.NestedClass$NestedInnerClass]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: {  "innerClass" : {    "nestedInnerClass2" : {      "nestedString" : "ABC",      "nestedHashMap" : {        "keyStr" : "12345"      }    },    "nestedInnerClass3" : {      "nestedString" : null    }  }}; line: 1, column: 24] (through reference chain: com.test.jackson.NestedClassChild["innerClass"])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1106)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:296)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:133)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:520)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:95)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:258)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:125)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3736)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2726)
    at com.test.jackson.NestedClassFromJson.main(NestedClassFromJson.java:21)

=================================

Nested MixIn which I tried but didn't work:

public abstract class NestedMixIn {

    @JsonCreator
    public NestedMixIn(@JsonProperty("innerClass") NestedInnerClass innerClass ) {
    }

    public static class SourceIdInnerMixin{

        @JsonCreator
        public SourceIdInnerMixin(@JsonProperty("nestedInnerClass2") NestedInnerClass2 nestedInnerClass2, 
                @JsonProperty("nestedInnerClass3") NestedInnerClass3 nestedInnerClass3) {
        }
    }
}

==========================

If I make the inner classes static , it works but since it is 3rd party class, I can't change it.

Will appreciate your help !!!


回答1:


In your example, I did not notice any relation between parent class and nested classes. Also you mentioned you can change it to static and it works, so all we need to do, is provide an instance of inner class to deserialisation process. By default Jackson uses com.fasterxml.jackson.databind.deser.BeanDeserializer to map JSON Object to a given class. We can extend it and register suppliers to instantiate objects.

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.function.Supplier;

public class JsonNestedApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        SimpleModule nestedModule = new SimpleModule();
        nestedModule.setDeserializerModifier(new NestedBeanDeserializerModifier());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(nestedModule);
        // other configuration

        NestedClass nestedClass = mapper.readValue(jsonFile, NestedClass.class);
        System.out.println(nestedClass);
    }
}

class NestedBeanDeserializerModifier extends BeanDeserializerModifier {

    private final NestedClass parent = new NestedClass();
    private final Map<Class, Supplier> availableSuppliers = new HashMap<>();

    public NestedBeanDeserializerModifier() {
        availableSuppliers.put(NestedClass.NestedInnerClass2.class, () -> parent.new NestedInnerClass2());
        availableSuppliers.put(NestedClass.NestedInnerClass3.class, () -> parent.new NestedInnerClass3());
    }

    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        final Supplier supplier = availableSuppliers.get(beanDesc.getBeanClass());
        if (supplier != null) {
            return new NestedBeanDeserializer((BeanDeserializerBase) deserializer, supplier);
        }

        return deserializer;
    }
}

class NestedBeanDeserializer extends BeanDeserializer {

    private final Supplier supplier;

    protected NestedBeanDeserializer(BeanDeserializerBase src, Supplier supplier) {
        super(src);
        this.supplier = Objects.requireNonNull(supplier);
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        return super.deserialize(p, ctxt, supplier.get());
    }
}

Above code should deserialise JSON payload with success.




回答2:


As suggested above : Solution is to extend the BeanDeserializer class.

======================

Supplier Interface:

public interface Supplier<T> {

    T get();
}

======================

BeanDeserializerModifier :

public class NestedBeanDeserializerModifier extends BeanDeserializerModifier {

    private NestedClass parent = new NestedClass();
    private Map<Class<?>, Supplier<?>> availableSuppliers = new HashMap<Class<?>, Supplier<?>>();

    public NestedBeanDeserializerModifier() {
        availableSuppliers.put(NestedClass.NestedInnerClass.class, new Supplier<NestedClass.NestedInnerClass>() {
            public NestedClass.NestedInnerClass get() {
                return parent.new NestedInnerClass();
            }
        });
        availableSuppliers.put(NestedClass.NestedInnerClass2.class, new Supplier<NestedClass.NestedInnerClass2>() {
            public NestedClass.NestedInnerClass2 get() {
                return parent.new NestedInnerClass2();
            }
        });
        availableSuppliers.put(NestedClass.NestedInnerClass3.class, new Supplier<NestedClass.NestedInnerClass3>() {
            public NestedClass.NestedInnerClass3 get() {
                return parent.new NestedInnerClass3();
            }

        });
    }

    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        final Supplier<?> supplier = availableSuppliers.get(beanDesc.getBeanClass());
        if (supplier != null) {
            return new NestedBeanDeserializer((BeanDeserializerBase) deserializer, supplier);
        }

        return deserializer;
    }
}

======================

BeanDeserializer :

public class NestedBeanDeserializer extends BeanDeserializer {

        private static final long serialVersionUID = 1L;
        private Supplier<?> supplier;

        protected NestedBeanDeserializer(BeanDeserializerBase src, Supplier<?> supplier) {
            super(src);
            this.supplier = requireNonNull(supplier);
        }

        @Override
        public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            return super.deserialize(p, ctxt, supplier.get());
        }

        private static <T> T requireNonNull(T obj) {
            if (obj == null)
                throw new NullPointerException();
            return obj;
        }
    }

====================

Output class using above deserializer :

public class NestedClassFromJson {

    public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.writerWithDefaultPrettyPrinter();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        SimpleModule nestedModule = new SimpleModule();
        nestedModule.setDeserializerModifier(new NestedBeanDeserializerModifier());
        mapper.registerModule(nestedModule);

        NestedClass objectNested = mapper.readValue(getPostBodyAsJSON(), NestedClassChild.class);
        System.out.println(mapper.writeValueAsString(objectNested));
    }

    private static String getPostBodyAsJSON() {
        StringBuffer postBody = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = new BufferedReader(new FileReader(new File("json/testNested.json")));
            while ((line = reader.readLine()) != null)
                postBody.append(line);
        } catch (IOException e) {
            throw new RuntimeException("Issue Occured While Reading POST Body", e);
        }

        return postBody.toString();
    }
}


来源:https://stackoverflow.com/questions/61532101/need-mixin-resolution-for-non-static-inner-class-using-objectmapper-java-6

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!