Cannot deserialize from Object value (no delegate- or property-based Creator) using Jackson

21,675

Solution 1

You have to consider few cases:

  • message field in JSON is primitive String. On POJO level it is an MessageSubscription object.
  • message value in JSON contains unquoted property names which is illegal but Jackson handles them as well.
  • If constructor does not fit to JSON we need to configure it using annotations.

To handle unquoted names we need to enable ALLOW_UNQUOTED_FIELD_NAMES feature. To handle mismatch between JSON payload and POJO we need to implement custom deserialiser for MessageSubscription class.

Custom deserialiser could look like this:

class MessageSubscriptionJsonDeserializer extends JsonDeserializer<MessageSubscription> {
    @Override
    public MessageSubscription deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        final String value = p.getValueAsString();
        final Map<String, String> map = deserializeAsMap(value, (ObjectMapper) p.getCodec(), ctxt);

        return new MessageSubscription(map.get("Message"), map.get("SubscriptionUID"));
    }

    private Map<String, String> deserializeAsMap(String value, ObjectMapper mapper, DeserializationContext ctxt) throws IOException {
        final MapType mapType = ctxt.getTypeFactory().constructMapType(Map.class, String.class, String.class);
        return mapper.readValue(value, mapType);
    }
}

Now, we need to customise DeserializeSubscription's constructor:

@Data
class DeserializeSubscription {

    private String code;
    private String reason;
    private MessageSubscription message;

    @JsonCreator
    public DeserializeSubscription(
            @JsonProperty("code") String code,
            @JsonProperty("reason") String reason,
            @JsonProperty("message") @JsonDeserialize(using = MessageSubscriptionJsonDeserializer.class) MessageSubscription message) {
        super();
        this.code = code;
        this.reason = reason;
        this.message = message;
    }
}

Example how to use it:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.type.MapType;
import lombok.Data;

import java.io.File;
import java.io.IOException;
import java.util.Map;

public class JsonPathApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
        mapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);

        DeserializeSubscription value = mapper.readValue(jsonFile, DeserializeSubscription.class);
        System.out.println(value);
    }
}

For provided JSON payload above example prints:

DeserializeSubscription(code=null, reason=subscription yet available, message=MessageSubscription(message=subscription yet available, subscriptionUID=46b62920-c519-4555-8973-3b28a7a29463))

Solution 2

The message is pretty clear: (no Creators, like default construct, exist)

you need to add a no argument constructor to the class or the NoArgsConstructor annotation:

@Data
public class DeserializeSubscription {
  public DeserializeSubscription (){}

or

@NoArgsConstructor
@Data
public class DeserializeSubscription {
Share:
21,675
Scripta14
Author by

Scripta14

Updated on July 09, 2022

Comments

  • Scripta14
    Scripta14 almost 2 years

    I'm trying to deserialise below JSON payload with Jackson:

    {"code":null,"reason":"subscription yet available","message":"{ Message:\"subscription yet available\", SubscriptionUID:\"46b62920-c519-4555-8973-3b28a7a29463\" }"}
    

    but I'm getting this JsonMappingException:

    Cannot construct instance of `com.ids.utilities.DeserializeSubscription` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
     at [Source: (String)"{"code":null,"reason":"subscription yet available","message":"{ Message:\"subscription yet available\", SubscriptionUID:\"46b62920-c519-4555-8973-3b28a7a29463\" }"}"; line: 1, column: 2]
    

    I've created two classes. The first class:

    import lombok.Data;
    
    @Data
    public class DeserializeSubscription {
    
        private String code;
        private String reason;
        private MessageSubscription message;
    
    
        public DeserializeSubscription(String code, String reason, MessageSubscription message) {
            super();
            this.code = code;
            this.reason = reason;
            this.message = message;
        }
    

    and the second class

    import lombok.Data;
    
    @Data
    public class MessageSubscription {
    
        private String message;
        private String subscriptionUID;
    
    
        public MessageSubscription(String message, String subscriptionUID) {
            super();
            this.message = message;
            this.subscriptionUID = subscriptionUID;
        }
    
    

    In the main class:

                     try 
                     {
    
                        ObjectMapper mapper = new ObjectMapper();
                        mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
                        DeserializeSubscription desSub=null;
    
                        desSub=mapper.readValue(e.getResponseBody(), DeserializeSubscription.class);
    
                        System.out.println(desSub.getMessage().getSubscriptionUID());
                     }
                     catch (JsonParseException e1) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                     }
                     catch (JsonMappingException e1) {
                         System.out.println(e1.getMessage());
                            e.printStackTrace();
                     }
                     catch (IOException e1) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                     }
    

    I've found this solution but I didn't work it https://facingissuesonit.com/2019/07/17/com-fasterxml-jackson-databind-exc-invaliddefinitionexception-cannot-construct-instance-of-xyz-no-creators-like-default-construct-exist-cannot-deserialize-from-object-value-no-delega/

    The jackson maven I'm using in my application

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.2</version>
        </dependency>
    
  • Scripta14
    Scripta14 about 4 years
    Sorry, I've tryied the first solution before and obtain the same result with tthe second solution
  • Jens
    Jens about 4 years
    @Scripta14* the same result with tthe second solution* and what ist the result?
  • Scripta14
    Scripta14 about 4 years
    The same exception: Cannot construct instance of `com.ids.utilities.DeserializeSubscription` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (String)"{"code":null,"reason":"subscription yet available","message":"{ Message:\"subscription yet available\", SubscriptionUID:\"46b62920-c519-4555-8973-3b28a7a29463\" }"}"; line: 1, column: 2]
  • Scripta14
    Scripta14 about 4 years
    Thanks a lot for your solution.. Unfortunately, I copied your code but I've got this error:Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Unexpected end-of-input within/between Object entries at [Source: (String)"{ Message:"; line: 1, column: 3] (through reference chain: test.exatest.DeserializeSubscription["message"]). I've used my json string
  • Michał Ziober
    Michał Ziober about 4 years
    @Scripta14, probably you have broken JSON payload. Also, have you enabled JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES feature? I have tested it for your payload and it works well. Which version of Jackson do you use?
  • Scripta14
    Scripta14 about 4 years
    above I wrote Jackson maven version that I'm using in my application. I've copied your code. The only on difference, I've used json string that I wrote above of this post.
  • Michał Ziober
    Michał Ziober about 4 years
    @Scripta14, have you escaped JSON payload properly? Payload: "{\"code\":null,\"reason\":\"subscription yet available\",\"message\":\"{ Message:\\\"subscription yet available\\\", SubscriptionUID:\\\"46b62920-c519-4555-8973-3b28a7a29463\\\" }\"}"
  • Scripta14
    Scripta14 about 4 years
    Thanks a lot ...It's working well your json payload, It was missing some escape
  • Michał Ziober
    Michał Ziober about 4 years
    @Scripta14, in case it works for you consider to accept and upvote my answer. How does accepting an answer work?