Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson JSON gives exception on collection of nested class

Jackson JSON has no problem serializing/deserializing this class:

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

but on this one:

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

I get this exception when deserializing:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class test.MyClass$Nested]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?) at [Source: java.io.StringReader@26653222; line: 1, column: 48] (through reference chain: test.MyClass["nestedList"]->java.util.ArrayList[0])

In the first case, Jackson has no problem dealing with an instance of a nested class, but not in the second case.

Must I write a custom deserializer?

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

}

like image 488
Piero dS Avatar asked Nov 14 '15 09:11

Piero dS


People also ask

How does Jackson read nested JSON?

A JsonNode is Jackson's tree model for JSON and it can read JSON into a JsonNode instance and write a JsonNode out to JSON. To read JSON into a JsonNode with Jackson by creating ObjectMapper instance and call the readValue() method. We can access a field, array or nested object using the get() method of JsonNode class.

What are the advantages disadvantages of nested classes?

Which among the following is the correct advantage/disadvantage of nested classes? Explanation: The use of nested classes makes the code more streamed towards a single concept. This allows to group the most similar and related classes together and makes it even more efficient and readable.

Is nested class good practice?

Nested Class can be used whenever you want to create more than once instance of the class or whenever you want to make that type more available. Nested Class increases the encapsulations as well as it will lead to more readable and maintainable code.


1 Answers

Looks like the recognition of non-static inner classes is done where they are properties directly on their containing bean (BeanDeserializerBase.java line 476 in 2.6.3). So an intervening Collection deserializer would go past that. A custom deserializer is likely the simplest option here.

Note that you can still use Jackson to read the properties of Nested, and just implement the construction of it yourself, in a custom deserializer only used when deserializing a list of Nested objects.

To do this, annotate the list like so:

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

and then use a custom deserializer that will:

  1. Look at the parsing context when called to find the containing MyClass instance.

  2. Encapsulate a default/root-level deserializer of Nested to delegate the work of deserializing the content to.

For example:

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;
  }
}
like image 131
araqnid Avatar answered Sep 19 '22 09:09

araqnid