H.Defesene
H.Defesene

Reputation: 21

Why spring boot can deserialize class without default constructor?

I wonder why spring boot can deserialize the class which has no default constructor by Jackson's objectMapper,but when i mannually using objectMapper in unit test,it can not deserialize(com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of xxx (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator) ).

Here is my controller:

  @PostMapping("/precise")
  public Resp<List<MatchedOutLineResponseDTO>> getPreciseMatchedOutLine(
  @RequestBody List<MatchedOutLineRequestDTO> request) 

Here is my pojo:

@Getter
public class MatchedOutLineRequestDTO {

   private String uuid;
   private JSONObject outLineGeometry;

   public MatchedOutLineRequestDTO(String uuid, JSONObject outLineGeometry) {
     this.uuid = uuid;
     this.outLineGeometry = outLineGeometry;
   }
 }

can someone tell me the reason?

Upvotes: 1

Views: 4321

Answers (1)

Sunit Chatterjee
Sunit Chatterjee

Reputation: 322

  1. Json Serialization (Object to JSON)
    • does not requires a no-argument constructor
    • only requires accessor methods (get...) for properties you want to expose
  2. Json Deserialization (JSON to Object)
    • requires a no-argument constructor and setters because the object mapper first creates the class using the no-args constructor and then uses the setters to set the field values.

Consider the code example below

public class Test {
    @Getter
    static class Dummy {
        private String uuid;
        private Date date;
        private JSONObject jsonObject;

        public Dummy(String uuid, Date date, JSONObject jsonObject) {
            this.uuid = uuid;
            this.date = date;
            this.jsonObject = jsonObject;
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("Name", "John Doe");
        jsonObject.put("Age", "Unknown");
        Dummy dummyObj = new Dummy(UUID.randomUUID().toString(), new Date(), jsonObject);

        String jsonStr = mapper.writeValueAsString(dummyObj);
        System.out.println("Serialized JSON = " + jsonStr);
    }
}

If you run this code, the only serialization error you will get is No serializer found for class org.json.JSONObject

The problem is the serialization of the JSONObject class.

The default configuration of ObjectMapper instance is to only access properties that are public fields or have public getters.

Hence this is problem in serialization of org.json.JSONObject.

There can be few workarounds

  1. Remove JsonObject and use a Map instead.

  2. Configure your object mapper to access Private fields by adding this:

    mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    

You will see the output like this

{
    "uuid": "15f37c75-9a82-476b-a725-90f3742d3de1",
    "date": 1607961986243,
    "jsonObject": {
        "map": {
            "Age": "Unknown",
            "Name": "John Doe"
        }
    }
}
  1. Last option is to ignore failing fields (like JSONObject) and serialize remaining fields. You can do this by configuring the object mapper like this:

    mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    

If you do this you will see output like this below:

{
    "uuid": "82808d07-47eb-4f56-85ff-7a788158bbb5",
    "date": 1607962486862,
    "jsonObject": {}
}

Upvotes: 4

Related Questions