解析json类型的请求体的问题,包含一个字符串列表到Spring响应中的字符串通量

Posted

技术标签:

【中文标题】解析json类型的请求体的问题,包含一个字符串列表到Spring响应中的字符串通量【英文标题】:Problem Parsing request body of type json, containing a list of string to Flux of string in Spring reactive 【发布时间】:2021-08-04 01:05:44 【问题描述】:

我有一个如下的 DTO:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import reactor.core.publisher.Flux;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class InternetPackageDto 
    private String id;

    private String name;

    private String termsAndConditions;

    private String price;

    private Flux<String> packageAttributes;

    private Flux<String> extras;

还有一个数据库对象如下:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import reactor.core.publisher.Flux;

@Document("internet_packages")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class InternetPackage 
    @Id
    private String id;

    private String name;

    private String termsAndConditions;

    private String price;

    private Flux<StoreableAttribute> attributes;

    private Flux<StoreableAttribute> extras;

StorableAttribute 数据库模型如下:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document("package_attributes")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StoreableAttribute 
    @Id
    private String id;

    private String name;

    private String description;


在数据对象上,字段:Flux&lt;StoreableAttribute&gt; attributesFlux&lt;StoreableAttribute&gt; extras 与包对象一起存储在单独的集合中。并由映射器处理如下:

 public InternetPackage fromDto(InternetPackageDto dto) 
        var internetPackage = new InternetPackage();

        internetPackage.setName(dto.getName());
        internetPackage.setPrice(dto.getPrice());
        internetPackage.setId(dto.getId());
        internetPackage.setExtras(this.resolePackageExtras(dto));
        internetPackage.setAttributes(this.resolePackageAttributes(dto));

        return internetPackage;
    

  private Flux<StoreableAttribute> resolePackageExtras(InternetPackageDto dto) 
        return this.storeableAttributeService.resolveAttributes(dto.getExtras());
    

对于额外的属性也是如此。

还有一个简单的控制器方法如下:

    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE, consumes =  MediaType.APPLICATION_JSON_VALUE)
    public Mono<InternetPackageDto> update(@RequestBody InternetPackageDto incomingPackageDto) 
        return this.packageService
                .updatePackage(this.dtoMapper.fromDto(incomingPackageDto))
                .map(this.dtoMapper::toDto);
    

当我发出发布请求时,我收到一条错误提示

org.springframework.core.codec.CodecException: Type definition error: [simple type, class reactor.core.publisher.Flux]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `reactor.core.publisher.Flux` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (io.netty.buffer.ByteBufInputStream); line: 2, column: 13] (through reference chain: com.example.api.dto.InternetPackageDto["extras"])

更多信息:

    我将InternetPackageDto 类用作请求对象和响应对象。 我使用的是Flux&lt;String&gt; 而不是List&lt;String&gt;,因为我不确定对列表进行阻止解析是否是个好主意。 属性分别存储和管理。 在更新或插入软件包期间;如果包含新的额外或属性,则 db 中的属性集合将随着新传入的额外和属性的插入而更新。

看来我可能犯了一个愚蠢的错误,因为我找不到有关此问题的太多信息,或者我做错了。

任何帮助将不胜感激。

【问题讨论】:

据我了解,杰克逊说“我无法序列化 Flux.class”。那是因为 Flux 不是一种数据结构,它是一个可以无限的流。 InternetPackage 是否存储在 mongo 中?你能指出我需要在属性中定义通量的库文档吗? 是的 InternetPackage 存储在数据库中。我不确定是否可以找到任何此类文档。我在这里所做的是尝试将InternetPackage 存储在数据库中,并更新从接收到的InternetPackageDto 传入的属性和附加信息(存储在另一个数据库集合中)。所以存储包,如果包有任何额外的和属性尚未存储,那么也将它们存储(在一个单独的集合到包集合中)。 好的,然后让我尝试更新我的答案。 你有机会尝试吗? 【参考方案1】:

我认为你应该这样做

public Mono<InternetPackageDto> toDto(InternetPackage entity) 
    var internetPackage = new InternetPackageDto();

    internetPackage.setName(entity.getName());
    internetPackage.setPrice(entity.getPrice());
    internetPackage.setId(entity.getId());


    return Mono.zip(Mono.just(internetPackage), entity.getExtras().collectList(), entity.getAttributes().collectList())
            .flatMap(tu->
                var dto = tu.getT1();
                dto.setExtras(tu.getT2()); //To make it work in my local i made entity.getAttributes() as Flux<String> so here you will probably need to use .stream().map(dbItem->dbItem.getPropertyName())
                dto.setPackageAttributes(tu.getT2());
                return Mono.just(dto);
            );

【讨论】:

谢谢沃瓦。但是文件packageAttributesextras 存储在数据库中,因为它们也可以独立管理。因此,每次添加包时,映射器都会查看提到的字段是否已存储在 db 中,如果尚未存储在 db 中,则将插入它们。更改解析器以使用 List 看起来像这样:this.storeableAttributeService.resolveAttributes(Flux.fromIterable(dto.getExtras())).collectList().block(); 我想避免这个阻塞调用。 @zambliner 立即查看

以上是关于解析json类型的请求体的问题,包含一个字符串列表到Spring响应中的字符串通量的主要内容,如果未能解决你的问题,请参考以下文章

mysql中的json数据类型

如何使用 Newtonsoft.Json 将包含数组数组的 json 对象解析为 C# 中的对象列表?

requestsip跳转域名

JSON解析器json-c

python爬虫和测试的区别

如何在没有外部库的情况下解析包含对象列表的对象列表