0

I have a PSQL table with one column containing jsonb data:

CREATE TABLE IF NOT EXISTS user (
    user_name VARCHAR(255) NOT NULL,
    user_email VARCHAR(255) NOT NULL,
    user_address jsonb NOT NULL
)

I have the following java object for that:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.Builder;
import lombok.Value;

@Value
@Builder
public class User {
    private String userName;
    private String userEmail;
    private UserAddress address;

    @Value
    @Builder
    @JsonDeserialize(builder = UserAddress.UserAddressBuilder.class)
    public static class UserAddress {
        @JsonProperty("street")
        private String street;
        @JsonProperty("house_number")
        private String houseNumber;

        @JsonPOJOBuilder(withPrefix = "")
        public static class UserAddressBuilder {
            private String street = null;
            private String houseNumber = null;

            public UserAddressBuilder street(String street) {
               this.street = street;

               return this;
            }

            public UserAddressBuilder houseNumber(String houseNumber) {
                this.houseNumber = houseNumber;

                return this;
            }
        }
    }
}

When I now build the User from a ResultSet:

return User.builder()
        .userName(rs.getString(SqlConstants.TableUser.USER_NAME))
        .userEmail(rs.getString(SqlConstants.TableUser.USER_EMAIL))
        .userAddress(mapper.convertValue(rs.getString(SqlConstants.TableUser.USER_ADDRESS), User.UserAddress.class))
        .build();

I always get the exception:

Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of my.package.model.User$UserAddress$UserAddressBuilder (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('"{\"street\":\"Some Street\",\"house_number\":\"Some House Number\"}')

I cannot find the source of this problem since the fields are matching, arent they? I also tried the approach without @Builder and used @Data, @AllArgsConstructor and @NoArgsConstructor instead.

I get the same result when using readValue instead of convertValue

pirho
  • 11,565
  • 12
  • 43
  • 70
Mulgard
  • 9,877
  • 34
  • 129
  • 232

1 Answers1

1

As mentioned in comments using

.userAddress(mapper.readValue(rs.getString(SqlConstants.TableUser.USER_ADDRESS), 
                                              User.UserAddress.class))

should work just fine at least it does for me. However after that change you might run into:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "house_number" ...

(instead of the original error) which will be fixed by small change to your UserAddressBuilder:

@JsonProperty("house_number") // this is also needed to map correctly
public UserAddressBuilder houseNumber(String houseNumber) { ...

From Javadoc:

public T convertValue(Object fromValue, Class toValueType)
throws IllegalArgumentException

Convenience method for doing two-step conversion from given value, into instance of given value type. This is functionality equivalent to first serializing given value into JSON, then binding JSON data into value of given type, but may be executed without fully serializing into JSON. Same converters (serializers, deserializers) will be used as for data binding, meaning same object mapper configuration works

while readValue is for reading from string, stream, reader into a object value. With convertValue(..) you might need converters defined but in your case it is not needed.

pirho
  • 11,565
  • 12
  • 43
  • 70