14

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

}

9
  • try creating a object of list or atleast set public List<Nested> nestedList = null; Commented Nov 14, 2015 at 9:46
  • 1
    try to make inner class static. Commented Nov 14, 2015 at 9:50
  • nestedList = null does not work Commented Nov 14, 2015 at 9:52
  • 1
    Added test code in question Commented Nov 14, 2015 at 12:22
  • 1
    Thanks for the test code. About static for your nested class: it would avoid the issue you have here and the argument "I don't want to make the inner class accessible outside the outer class" is not affected by static. Currently Nested is package-private in your example and you could keep it that way. Commented Nov 14, 2015 at 13:53

1 Answer 1

13

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; } } 
Sign up to request clarification or add additional context in comments.

2 Comments

Spot on! I actually ended up doing something not so clean - using @jsondeserialize, but with a custom builder that would just generate a Nested from a temp MyClass, and return it. But this is the textbok way. Thanks!
Very super solution

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.