Spring对象映射器covertValue方法不转换某些字段

Posted

技术标签:

【中文标题】Spring对象映射器covertValue方法不转换某些字段【英文标题】:Spring object mapper covertValue method doesn't convert some fields 【发布时间】:2020-06-16 23:01:57 【问题描述】:

我有一个 Spring Boot 2 应用程序,它使用 ObjectMapper.convertValue 方法在实体和 DTO 之间进行转换。

我一直在试图理解为什么该方法不转换某些字段,具体来说,看看以下场景:

产品实体:

@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;
    

产品 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;
    

执行以下代码时,每个字段都会自动转换:

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

category 除外。所以设置了product变量中的category,而转换后得到的productDTO.category字段为null。

类别实体:

@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<>();

类别 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;
    

所以问题是,为什么 ObjectMapper 也不能自动转换类别字段?是否有任何条件阻止它发生?因为根本没有抛出任何错误。

下面是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 版本 2.10.1

谢谢

【问题讨论】:

【参考方案1】:

所以代码中的罪魁祸首是@JsonBackReference。由于@JsonBackReference注解分配给

    @JsonBackReference
    private Category category;

此类别在序列化时会自动删除。来自this reference:

@JsonManagedReference 是引用的前向部分——正常序列化的部分。 @JsonBackReference 是引用的后面部分——它将从序列化中省略。

所以不要尝试使用@JsonBackReference@JsonManagedReference

希望这能解决您的问题。

【讨论】:

我尝试添加和删除它。不用找了。问题是转换后整个类别对象为空,而不仅仅是它的字段 能否同时检查产品内部json中是否存在category值? 这个调用:String s = objectMapper.writeValueAsString(product);产生这个输出: "id":1,"name":"Denimearrings","description":"empty description","price":10.00,"weight":1000,"stock":1000,"imageUrl": "resources/images/earrings/E-cheer-yellow.jpg","thumbnails":["id":3,"imageUrl":"resources/images/earrings/E-cheer-yellow-big.jpg" ] 所以这里也没有分类 所以类别 null 是预期的。尝试使用一些具有类别值的值。 输入 var products 中的类别不为空。它很有价值,所以看起来它被某种方式跳过了。在转换发生之前,我用产品对象的图片更新了答案。已更新。

以上是关于Spring对象映射器covertValue方法不转换某些字段的主要内容,如果未能解决你的问题,请参考以下文章

Spring映射器适配器解析器

在 Spring Boot 端点上使用自定义杰克逊映射器

如何修改 Spring Cloud AWS 用来反序列化 SQS 消息的对象映射器?

行映射器/转换器在spring boot中将对象数组列表转换为json

JavaMyBatis与Spring框架整合

Spring框架整合mybais框架-注入映射器實現