Jackson JSON дает исключение на коллекцию вложенного класса


Jackson JSON не имеет проблем с сериализацией / десериализацией этого класса:

public class MyClass {
   public class Nested {
      public String string;
      public Nested() {}
   }
   public Nested nestedVar;
}

Но на этот раз:

public class MyClass {
   class Nested {
      public String string;
      public Nested() {}
   }
   public Nested nestedVar;
   public List<Nested> nestedList;
}

Я получаю это исключение при десериализации:

Ком.fasterxml.Джексон.база данных.JsonMappingException: не найден подходящий конструктор для type [simple type, class test.MyClass$Nested]: не удается создать экземпляр из объекта JSON (отсутствует конструктор по умолчанию или создатель, или, возможно, нужно добавить / включить информацию о типе?) в [источник: java.io.StringReader@26653222; строка: 1, столбец: 48] (через цепочку ссылок: тест.MyClass ["nestedList"] - > java.утиль.ArrayList[0])

В первом случае Джексон не имеет проблем с экземпляром вложенного класса, но не во втором случае.

Должен ли я написать пользовательский десериализатор?

Тестовый код (Джексон 2.6.3):

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

public class ATest {

   public static void main(String[] args) throws IOException {

      ObjectMapper mapper = new ObjectMapper();

      StringWriter sw = new StringWriter();

      MyClass myClass = new MyClass();

      MyClass.Nested nestedVar = myClass.new Nested();

      List<MyClass.Nested> nestedList = new ArrayList<>();

      nestedList.add(nestedVar);

      myClass.nestedList =nestedList;

      myClass.nestedVar = nestedVar;

      mapper.writeValue(sw, myClass);

      System.out.println(sw.toString());

      StringReader sr = new StringReader(sw.toString());

      MyClass z = mapper.readValue(sr, MyClass.class);
}

}

1 4

1 ответ:

Похоже, что распознавание нестатических внутренних классов выполняется там, где они являются свойствами непосредственно на содержащем их Бобе (BeanDeserializerBase.java строка 476 в 2.6.3). Так что промежуточный десериализатор коллекции прошел бы мимо этого. Пользовательский десериализатор, вероятно, самый простой вариант здесь.

Обратите внимание, что вы все еще можете использовать Джексона для чтения свойств Nested и просто реализовать его построение самостоятельно, в пользовательском десериализаторе только , используемом при десериализации списка Nested объекты.

Чтобы сделать это, аннотируйте список следующим образом:

    @JsonDeserialize(contentUsing = NestedDeserializer.class)
    public List<Nested> nestedList;

А затем используйте пользовательский десериализатор, который будет:

  1. Посмотрите на контекст синтаксического анализа при вызове, чтобы найти содержащий экземпляр MyClass.

  2. Инкапсулируйте десериализатор по умолчанию / корневого уровня Nested, чтобы делегировать ему работу по десериализации содержимого.

Например:

public static final class NestedDeserializer extends StdDeserializer<MyClass.Nested>
    implements ResolvableDeserializer {
  private JsonDeserializer<Object> underlyingDeserializer;

  public NestedDeserializer() {
    super(MyClass.Nested.class);
  }

  @Override
  public void resolve(DeserializationContext ctxt) throws JsonMappingException {
    underlyingDeserializer = ctxt
        .findRootValueDeserializer(ctxt.getTypeFactory().constructType(MyClass.Nested.class));
  }

  @Override
  public Nested deserialize(JsonParser p, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {
    JsonStreamContext ourContext = p.getParsingContext();
    JsonStreamContext listContext = ourContext.getParent();
    JsonStreamContext containerContext = listContext.getParent();
    MyClass container = (MyClass) containerContext.getCurrentValue();
    MyClass.Nested value = container.new Nested();
    // note use of three-argument deserialize method to specify instance to populate
    underlyingDeserializer.deserialize(p, ctxt, value);
    return value;
  }
}