Shaunyl
Shaunyl

Reputation: 594

Spring object mapper covertValue method doesn't convert some fields

I have a Spring Boot 2 application which uses the ObjectMapper.convertValue method to convert from/to Entities and DTOs.

I've been trying to understand why that method doesn't convert some of the fields, specifically, look at the following scenario:

Product entity:

@Entity
@Table(name = "product")
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@AllArgsConstructor
public class Product extends AbstractPersistable<Long> {

    @Column
    private String name;

    @Column
    private String description;

    @Column
    private BigDecimal price;

    @Column
    private int weight;

    @Column
    private int stock = 0;

    @Column(name = "image_url", length = 254, unique = true)
    private String imageUrl;

    @NotEmpty
    @Column(name = "banner_image_url", length = 254, unique = true)
    private String bannerImageUrl;

    @ManyToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "product_category_id")
    @JsonBackReference
    private Category category;

    @OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @OrderBy
    @JsonManagedReference
    private SortedSet<ProductThumbnail> thumbnails;

    public Product(Long id) {
        this.setId(id);
    }

    public Product(String name, String description, BigDecimal price, int weight) {
        this.name = name;
        this.description = description;
        this.price = price;
        this.weight = weight;
    }
}

Product DTO:

@Getter
@Setter
@NoArgsConstructor
public class ProductDTO {

    private Long id;

    private String name;

    private String description;

    private BigDecimal price;

    private int weight;

    private String imageUrl;

    private String bannerImageUrl;

    private CategoryDTO category;

    private SortedSet<ProductThumbnailDTO> thumbnails;

    public ProductDTO(@JsonProperty("id") Long id) {
        this.id = id;
    }

    @JsonCreator
    public ProductDTO(@JsonProperty("id") Long id,
                      @JsonProperty("name") String name,
                      @JsonProperty("description") String description,
                      @JsonProperty("price") BigDecimal price,
                      @JsonProperty("weight") int weight,
                      @JsonProperty("imageUrl") String imageUrl,
                      @JsonProperty("category") CategoryDTO category,
                      @JsonProperty("variants") Set<ProductVariantDTO> variants) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.price = price;
        this.weight = weight;
        this.imageUrl = imageUrl;
        this.category = category;
        this.variants = variants;
    }
}

Each field gets converted automatically when executing the folliwng code:

ProductDTO productDTO = objectMapper.convertValue(product, ProductDTO.class);

but with the exception of category. So category in the product variable is set, while the resulting productDTO.category field is null after the conversion.

Category entity:

@Entity
@Table(name = "product_category")
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@AllArgsConstructor
@Immutable
public class Category extends AbstractPersistable<Long> {

    @Column
    private String name;

    @Column
    private String description;

    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JsonManagedReference
    private Set<Product> products = new HashSet<>();
}

Category DTO:

@Data
@NoArgsConstructor
public class CategoryDTO {

    private String name;

    private String description;

    @JsonIgnore
    private Set<Product> products = new HashSet<>();

    @JsonCreator
    public CategoryDTO(@JsonProperty("name") String name, @JsonProperty("description") String description) {
        this.name = name;
        this.description = description;
    }
}

So the question is, why the ObjectMapper is not able to convert automatically the category field as well? Is there any condition that prevents it to happen? Because no error is thrown at all.

Following is the objectmapper bean:

@Bean
public ObjectMapper objectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
    objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    objectMapper.findAndRegisterModules();
    objectMapper.registerModules(module(), new Jdk8Module());
    return objectMapper;
}

jackson dep version 2.10.1

Thank you

Upvotes: 3

Views: 5123

Answers (1)

MD Ruhul Amin
MD Ruhul Amin

Reputation: 4502

So the main culprit in your code is the @JsonBackReference. As @JsonBackReference annotation is assigned to

    @JsonBackReference
    private Category category;

This category is auto removed while serialization. From this reference:

@JsonManagedReference is the forward part of reference – the one that gets serialized normally. @JsonBackReference is the back part of reference – it will be omitted from serialization.

So instead of @JsonBackReference try using: @JsonManagedReference.

Hope this solve your problem.

Upvotes: 1

Related Questions