Dan W
Dan W

Reputation: 5782

Jackson 3rd Party Class With No Default Constructor

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.

Upvotes: 35

Views: 26606

Answers (2)

M. Justin
M. Justin

Reputation: 21123

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

Upvotes: 1

Programmer Bruce
Programmer Bruce

Reputation: 66943

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.

Upvotes: 29

Related Questions