javapojojsonnode

Cannot construct instance of `com.domain.User` (no Creators, like default constructor, exist): cannot deserialize from Object value


I have a controller that accepts ObjectNode as @RequestBody.

That ObjectNode represents json with some user data

{
    "given_name":"ana",
    "family_name": "fabry",
    "email": "fabry@gmail.com",
    "password": "mypass",
    "gender": "FEMALE"
}

Controller.java

@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public JsonNode createUser(@RequestBody ObjectNode user){
        return userService.addUser(user);
 }

I want to get user as ObjectNode convert it to Java POJO save it to database and again return it as JsonNode.

UserServiceImpl.java

    private final UserRepository userRepository;
    private final UserMapper userMapper;

    @Override
    public JsonNode addUser(@RequestBody ObjectNode user) {
        try {
            return userMapper.fromJson(user)
                    .map(r -> {
                        final User created = userRepository.save(r);
                        return created;
                    })
                    .map(userMapper::toJson)
                    .orElseThrow(() -> new ResourceNotFoundException("Unable to find user"));
        } catch (RuntimeException re) {
            throw re;
        }
    }

To convert ObjectNode to POJO

I did this in my UserMapper class:

public Optional<User> fromJson(ObjectNode jsonUser) {
  User user = objectMapper.treeToValue(jsonUser, User.class);
}

Also, to write object to JsonNode I did this:

public JsonNode toJson(User user) {
        ObjectNode node = objectMapper.createObjectNode();
        node.put("email", user.email);
        node.put("password", user.password);
        node.put("firstName", user.firstName);
        node.put("lastName", user.firstName);
        node.put("gender", user.gender.value);
        node.put("registrationTime", user.registrationTime.toString());
        return node;
}

User.java

@Document(collection = "user")
@Builder
@AllArgsConstructor
public class User {

    @Indexed(unique = true)
    public final String email;
    @JsonProperty("password")
    public final String password;
    @JsonProperty("firstName")
    public final String firstName;
    @JsonProperty("lastName")
    public final String lastName;
    @JsonProperty("gender")
    public final Gender gender;
    @JsonProperty("registrationTime")
    public final Instant registrationTime;

    public static User createUser(
            String email,
            String password,
            String firstName,
            String lastName,
            Gender gender,
            Instant registrationTime){
        return new User(email, password, firstName, lastName, gender, registrationTime);
    }
}

When I run my application, this is the error I am receiving:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.domain.User` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

I have read about the error, and it seems this error occurs because Jackson library doesn't know how to create a model which doesn't have an empty constructor and the model contains a constructor with parameters which I annotated its parameters with @JsonProperty("fieldName"). But even after applying @JsonProperty("fieldName") I am still getting the same error.

I have defined ObjecatMapper as Bean

    @Bean
    ObjectMapper getObjectMapper(){
        return new ObjectMapper();
    }

What am I missing here?


Solution

  • I could reproduce the exception. Then I added an all-args constructor with each parameter annotated with the right @JsonProperty.

    @JsonCreator
    public User( 
        @JsonProperty("email") String email,
        @JsonProperty("password") String password,
        @JsonProperty("firstName") String firstName,
        @JsonProperty("lastName") String lastName,
        @JsonProperty("gender") String gender,
        @JsonProperty("registrationTime") Instant registrationTime){
                super();
                this.email = email;
                this.password = password;
                this.firstName = firstName;
                this.lastName = lastName;
                this.gender = gender;
                this.registrationTime = registrationTime;
    }
    

    Now, it creates the instance, but I get other mapping errors (Unrecognized field "given_name") which you should be able to resolve.