Требуется разрешение MixIn для не статичного c внутреннего класса с использованием ObjectMapper - Java 6 - PullRequest
1 голос
/ 30 апреля 2020

Я столкнулся с проблемой при использовании ObjectMapper для нестати c внутреннего класса. Мне нужно создать MixIn, чтобы он работал, но не смог найти решение. Ниже мой класс (который я не могу изменить) и MixIn, я попробовал. Нужна помощь для создания такого MixIn.

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

Базовый класс

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


}

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

Дочерний класс вложенного класса:

public class NestedClassChild extends NestedClass implements Serializable, Cloneable{

    private static final long serialVersionUID = 7022339501842754692L;

    public NestedClassChild() {}
}

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

Класс поддержки:

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

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

Для преобразования в JSON Полезная нагрузка:

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

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

Создано JSON полезных данных из вышеуказанного класса:

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

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

Класс для десериализации из 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();
    }
}

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

Но я получаю ниже Исключения (хотя у меня есть конструктор по умолчанию):

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)

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

Вложенный MixIn, который я пробовал, но не работал:

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

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

Если я сделаю внутренние классы stati c, это будет работать, но так как это 3-й класс вечеринки, я не могу его изменить.

Буду признателен за вашу помощь !!! * 104 7 *

Ответы [ 2 ]

1 голос
/ 02 мая 2020

Как указано выше: решение состоит в расширении класса BeanDeserializer.

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

Интерфейс поставщика:

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

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

Класс вывода с использованием вышеуказанного десериализатора:

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();
    }
}
1 голос
/ 02 мая 2020

В вашем примере я не заметил никакой связи между родительским классом и вложенными классами. Также вы упомянули, что можете изменить его на stati c, и он работает, поэтому все, что нам нужно сделать, - это предоставить экземпляр внутреннего класса процессу десериализации. По умолчанию Jackson использует com.fasterxml.jackson.databind.deser.BeanDeserializer для сопоставления JSON Object с данным классом. Мы можем расширить его и зарегистрировать поставщиков для создания экземпляров объектов.

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

Приведенный выше код должен десериализовать JSON полезную нагрузку с успехом.

...