34

I'm trying to use Jackson to read/write my POJOs to/from Json. As of right now, I've got it configured and working for my classes, except for a 3rd party class. When trying to read in the Json I get the error:

org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type

After a few quick google searches, it appears that my class needs either a default constructor or to override the default constructor with annotations. Unfortunately, the class in which this is failing is from a 3rd party library and that class does not have a default constructor and I obviously cannot over-write the code.

So my question is, is there anything I can do about this or am I just out of luck?

Thanks.

Community
  • 1
  • 1
Dan W
  • 5,718
  • 4
  • 33
  • 44
  • You could use BCEL, ASM, or something similar to engineer a new version of the 3rd party class file that does have a constructor. – jbranchaud Aug 07 '12 at 01:20

2 Answers2

29

You could make use of Jackson's Mix-Ins feature, coupled with the Creator feature. The Mix-Ins feature alleviates the need to annotate the original third-party code, and the Creator feature provides a mechanism for custom instance creation.

For yet more customization, it's not too involved to write a custom deserializer.

ykaganovich
  • 14,736
  • 8
  • 59
  • 96
Programmer Bruce
  • 64,977
  • 7
  • 99
  • 97
  • 4
    These links are for Jackson 1. For Jackson 2, you can add the mix-in directly to the `objectMapper`, `objectMapper.addMixInAnnotations(target, mixinSource)` ([source](https://github.com/FasterXML/jackson-docs/issues/2)) – kuporific Jul 16 '14 at 15:30
  • He is right, I used the mixin approach to deserialized a field of the type android.location.Location, which by the way does not have a default constructor (and I have not control over the class/3rd party). No problem to serialize but had the not default constructor exception while deserializing. I extended a mixin class from Location and used the JsonCreator annotation to implement a constructor using specific fields. Worked like a charm, thanks for showing me the way @Programmer Bruce – 1vand1ng0 May 31 '17 at 00:17
  • See here for more help on this: https://stackoverflow.com/questions/30080217/json-deserialisation-using-jackson-no-suitable-constructor-found-for-type-pro – user64141 Sep 29 '17 at 19:41
  • Above has been deprecated and changed yet again to a "fluent" API: `objectMapper.addMixIn(Target.class, MixIn.class);` – jordanpg Oct 26 '17 at 13:50
1

One approach is to implement a custom JsonDeserializer to create the instance, annotating the fields of the type with @JsonDeserialize. One advantage of this approach over e.g. mixins is that it does not require modifying the ObjectMapper.

The StdNodeBasedDeserializer class allows mapping from a JsonNode representing the value to the desired type.

Type lacking a constructor

public class ThirdPartyType {
    private String stringProperty;

    private int intProperty;

    private Object[] arrayProperty;

    public ThirdPartyType(String a, int b, Object[] c) {
        this.stringProperty = a;
        this.intProperty = b;
        this.arrayProperty = c;
    }
    
    // Getters and setters go here
}

Custom deserializer

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdNodeBasedDeserializer;

import java.io.IOException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.StreamSupport;

public class ThirdPartyTypeDeserializer 
        extends StdNodeBasedDeserializer<ThirdPartyType> {
    protected ThirdPartyTypeDeserializer() {
        super(ThirdPartyType.class);
    }

    @Override
    public ThirdPartyType convert(JsonNode root, DeserializationContext ctxt)
            throws IOException {
        return new ThirdPartyType(
                root.get("stringProperty").asText(null),
                root.get("intProperty").asInt(),
                StreamSupport.stream(
                        Spliterators.spliteratorUnknownSize(
                                root.get("arrayProperty").elements(),
                                Spliterator.ORDERED),
                        false).toArray());
    }
}

Type containing the third party type

public class EnclosingClass {
    @JsonDeserialize(using = ThirdPartyTypeDeserializer.class)
    private ThirdPartyType thirdPartyProperty;
    
    // Getters and setters go here
}

Retrieving the value

String json = "{\"thirdPartyProperty\": {"
        + "\"stringProperty\": \"A\", "
        + "\"intProperty\": 5, "
        + "\"arrayProperty\": [1, \"B\", false]"
        + "}}";
ObjectMapper objectMapper = new ObjectMapper();
EnclosingClass enclosingClass =
        objectMapper.readValue(json, EnclosingClass.class);
M. Justin
  • 14,487
  • 7
  • 91
  • 130