WebFlux WebClient with Generic Types

huangapple go评论71阅读模式
英文:

WebFlux WebClient with Generic Types

问题

这里是您请求的翻译内容:

我正在尝试使用 Spring WebFlux 中的 WebClient 创建客户端库。

服务器返回的响应是这样的 JSON:

***省略 JSON 内容***

***result*** 字段包含元素数组,这些元素可能因所使用的 API 而异。

有关该 API 的更多信息,请参阅 ServiceNow 产品的[表 API][1]。

正如您在文档中所看到的,API 路径类似于:GET /now/table/{tableName},其中 tableName 可以是不同的值,而结果元素的类型将取决于所获取的表。

因此,***result*** 字段内的响应元素可以由不同的名称或不同数量的属性组成。基本上,这是通过 HTTP 公开的数据库,因此每个表都可以由不同数量的列组成。

我的 GET 实现:

```java
public Mono<Result> get(){
    return client.get()
            .uri(uriBuilder -> uriBuilder
                    .path(this.tablePath)
                    .queryParam("active", "true")
                    .queryParam("state", "1")
                    .build())
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(Result.class);
}

POJOs:

@Data
public class ProjectTask {

    private String project;

}
@Data
public class Result<T> {

    List<T> result;

}

使用示例以获取错误:

List<ProjectTask> projectTasks = Objects.requireNonNull(
        client.table("pm_project_task")
                .get().block())
        .getResult();

for (ProjectTask task : projectTasks){
    System.out.println("========PROJECT=============== " + task.getProject());
}

我的运行时错误在 for (ProjectTask task : projectTasks){ 行:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to my.namespace.api.services.servicenow.model.ProjectTask

响应需要:

  • Result 的 POJO 必须使用泛型来管理不同的响应元素类型。

  • 如果无法完成第一个要点,请尝试提供一种解决方案,以使用可重用的 GET 实现获取不同类型的表。

  • 我不想从我的客户端返回 List,我的想法是此库仅返回 MonoFlux 对象。


注意:由于您要求只返回翻译好的部分,因此上述内容是根据您提供的文本进行的翻译。如果您有任何问题或需要进一步的帮助,请随时提问。
<details>
<summary>英文:</summary>
I&#39;m trying to make a Client Library with WebClient from Spring WebFlux.
Server return responses like this JSON:
```lang-json
{
&quot;result&quot;: [
{
&quot;parent&quot;: &quot;&quot;,
&quot;made_sla&quot;: &quot;true&quot;,
&quot;watch_list&quot;: &quot;&quot;,
&quot;upon_reject&quot;: &quot;cancel&quot;,
&quot;sys_updated_on&quot;: &quot;2016-01-19 04:52:04&quot;,
&quot;approval_history&quot;: &quot;&quot;,
&quot;number&quot;: &quot;PRB0000050&quot;,
&quot;sys_updated_by&quot;: &quot;glide.maint&quot;,
&quot;opened_by&quot;: {
&quot;link&quot;: &quot;https://instance.service-now.com/api/now/table/sys_user/glide.maint&quot;,
&quot;value&quot;: &quot;glide.maint&quot;
},
&quot;user_input&quot;: &quot;&quot;,
&quot;sys_created_on&quot;: &quot;2016-01-19 04:51:19&quot;,
&quot;sys_domain&quot;: {
&quot;link&quot;: &quot;https://instance.service-now.com/api/now/table/sys_user_group/global&quot;,
&quot;value&quot;: &quot;global&quot;
},
&quot;state&quot;: &quot;4&quot;,
&quot;sys_created_by&quot;: &quot;glide.maint&quot;,
&quot;knowledge&quot;: &quot;false&quot;,
&quot;order&quot;: &quot;&quot;,
&quot;closed_at&quot;: &quot;2016-01-19 04:52:04&quot;,
&quot;cmdb_ci&quot;: {
&quot;link&quot;: &quot;https://instance.service-now.com/api/now/table/cmdb_ci/55b35562c0a8010e01cff22378e0aea9&quot;,
&quot;value&quot;: &quot;55b35562c0a8010e01cff22378e0aea9&quot;
},
&quot;delivery_plan&quot;: &quot;&quot;,
&quot;impact&quot;: &quot;3&quot;,
&quot;active&quot;: &quot;false&quot;,
&quot;work_notes_list&quot;: &quot;&quot;,
&quot;business_service&quot;: &quot;&quot;,
&quot;priority&quot;: &quot;4&quot;,
&quot;sys_domain_path&quot;: &quot;/&quot;,
&quot;time_worked&quot;: &quot;&quot;,
&quot;expected_start&quot;: &quot;&quot;,
&quot;rejection_goto&quot;: &quot;&quot;,
&quot;opened_at&quot;: &quot;2016-01-19 04:49:47&quot;,
&quot;business_duration&quot;: &quot;1970-01-01 00:00:00&quot;,
&quot;group_list&quot;: &quot;&quot;,
&quot;work_end&quot;: &quot;&quot;,
&quot;approval_set&quot;: &quot;&quot;,
&quot;wf_activity&quot;: &quot;&quot;,
&quot;work_notes&quot;: &quot;&quot;,
&quot;short_description&quot;: &quot;Switch occasionally drops connections&quot;,
&quot;correlation_display&quot;: &quot;&quot;,
&quot;delivery_task&quot;: &quot;&quot;,
&quot;work_start&quot;: &quot;&quot;,
&quot;assignment_group&quot;: &quot;&quot;,
&quot;additional_assignee_list&quot;: &quot;&quot;,
&quot;description&quot;: &quot;Switch occasionally drops connections&quot;,
&quot;calendar_duration&quot;: &quot;1970-01-01 00:02:17&quot;,
&quot;close_notes&quot;: &quot;updated firmware&quot;,
&quot;sys_class_name&quot;: &quot;problem&quot;,
&quot;closed_by&quot;: &quot;&quot;,
&quot;follow_up&quot;: &quot;&quot;,
&quot;sys_id&quot;: &quot;04ce72c9c0a8016600b5b7f75ac67b5b&quot;,
&quot;contact_type&quot;: &quot;phone&quot;,
&quot;urgency&quot;: &quot;3&quot;,
&quot;company&quot;: &quot;&quot;,
&quot;reassignment_count&quot;: &quot;&quot;,
&quot;activity_due&quot;: &quot;&quot;,
&quot;assigned_to&quot;: &quot;&quot;,
&quot;comments&quot;: &quot;&quot;,
&quot;approval&quot;: &quot;not requested&quot;,
&quot;sla_due&quot;: &quot;&quot;,
&quot;comments_and_work_notes&quot;: &quot;&quot;,
&quot;due_date&quot;: &quot;&quot;,
&quot;sys_mod_count&quot;: &quot;1&quot;,
&quot;sys_tags&quot;: &quot;&quot;,
&quot;escalation&quot;: &quot;0&quot;,
&quot;upon_approval&quot;: &quot;proceed&quot;,
&quot;correlation_id&quot;: &quot;&quot;,
&quot;location&quot;: &quot;&quot;
},
{
&quot;parent&quot;: &quot;&quot;,
&quot;made_sla&quot;: &quot;true&quot;,
&quot;watch_list&quot;: &quot;&quot;,
&quot;upon_reject&quot;: &quot;cancel&quot;,
&quot;sys_updated_on&quot;: &quot;2016-01-19 04:52:04&quot;,
&quot;approval_history&quot;: &quot;&quot;,
&quot;number&quot;: &quot;PRB0000050&quot;,
&quot;sys_updated_by&quot;: &quot;glide.maint&quot;,
&quot;opened_by&quot;: {
&quot;link&quot;: &quot;https://instance.service-now.com/api/now/table/sys_user/glide.maint&quot;,
&quot;value&quot;: &quot;glide.maint&quot;
},
&quot;user_input&quot;: &quot;&quot;,
&quot;sys_created_on&quot;: &quot;2016-01-19 04:51:19&quot;,
&quot;sys_domain&quot;: {
&quot;link&quot;: &quot;https://instance.service-now.com/api/now/table/sys_user_group/global&quot;,
&quot;value&quot;: &quot;global&quot;
},
&quot;state&quot;: &quot;4&quot;,
&quot;sys_created_by&quot;: &quot;glide.maint&quot;,
&quot;knowledge&quot;: &quot;false&quot;,
&quot;order&quot;: &quot;&quot;,
&quot;closed_at&quot;: &quot;2016-01-19 04:52:04&quot;,
&quot;cmdb_ci&quot;: {
&quot;link&quot;: &quot;https://instance.service-now.com/api/now/table/cmdb_ci/55b35562c0a8010e01cff22378e0aea9&quot;,
&quot;value&quot;: &quot;55b35562c0a8010e01cff22378e0aea9&quot;
},
&quot;delivery_plan&quot;: &quot;&quot;,
&quot;impact&quot;: &quot;3&quot;,
&quot;active&quot;: &quot;false&quot;,
&quot;work_notes_list&quot;: &quot;&quot;,
&quot;business_service&quot;: &quot;&quot;,
&quot;priority&quot;: &quot;4&quot;,
&quot;sys_domain_path&quot;: &quot;/&quot;,
&quot;time_worked&quot;: &quot;&quot;,
&quot;expected_start&quot;: &quot;&quot;,
&quot;rejection_goto&quot;: &quot;&quot;,
&quot;opened_at&quot;: &quot;2016-01-19 04:49:47&quot;,
&quot;business_duration&quot;: &quot;1970-01-01 00:00:00&quot;,
&quot;group_list&quot;: &quot;&quot;,
&quot;work_end&quot;: &quot;&quot;,
&quot;approval_set&quot;: &quot;&quot;,
&quot;wf_activity&quot;: &quot;&quot;,
&quot;work_notes&quot;: &quot;&quot;,
&quot;short_description&quot;: &quot;Switch occasionally drops connections&quot;,
&quot;correlation_display&quot;: &quot;&quot;,
&quot;delivery_task&quot;: &quot;&quot;,
&quot;work_start&quot;: &quot;&quot;,
&quot;assignment_group&quot;: &quot;&quot;,
&quot;additional_assignee_list&quot;: &quot;&quot;,
&quot;description&quot;: &quot;Switch occasionally drops connections&quot;,
&quot;calendar_duration&quot;: &quot;1970-01-01 00:02:17&quot;,
&quot;close_notes&quot;: &quot;updated firmware&quot;,
&quot;sys_class_name&quot;: &quot;problem&quot;,
&quot;closed_by&quot;: &quot;&quot;,
&quot;follow_up&quot;: &quot;&quot;,
&quot;sys_id&quot;: &quot;04ce72c9c0a8016600b5b7f75ac67b5b&quot;,
&quot;contact_type&quot;: &quot;phone&quot;,
&quot;urgency&quot;: &quot;3&quot;,
&quot;company&quot;: &quot;&quot;,
&quot;reassignment_count&quot;: &quot;&quot;,
&quot;activity_due&quot;: &quot;&quot;,
&quot;assigned_to&quot;: &quot;&quot;,
&quot;comments&quot;: &quot;&quot;,
&quot;approval&quot;: &quot;not requested&quot;,
&quot;sla_due&quot;: &quot;&quot;,
&quot;comments_and_work_notes&quot;: &quot;&quot;,
&quot;due_date&quot;: &quot;&quot;,
&quot;sys_mod_count&quot;: &quot;1&quot;,
&quot;sys_tags&quot;: &quot;&quot;,
&quot;escalation&quot;: &quot;0&quot;,
&quot;upon_approval&quot;: &quot;proceed&quot;,
&quot;correlation_id&quot;: &quot;&quot;,
&quot;location&quot;: &quot;&quot;
},
{
&quot;parent&quot;: &quot;&quot;,
&quot;made_sla&quot;: &quot;true&quot;,
&quot;watch_list&quot;: &quot;&quot;,
&quot;upon_reject&quot;: &quot;cancel&quot;,
&quot;sys_updated_on&quot;: &quot;2016-01-19 04:52:04&quot;,
&quot;approval_history&quot;: &quot;&quot;,
&quot;number&quot;: &quot;PRB0000050&quot;,
&quot;sys_updated_by&quot;: &quot;glide.maint&quot;,
&quot;opened_by&quot;: {
&quot;link&quot;: &quot;https://instance.service-now.com/api/now/table/sys_user/glide.maint&quot;,
&quot;value&quot;: &quot;glide.maint&quot;
},
&quot;user_input&quot;: &quot;&quot;,
&quot;sys_created_on&quot;: &quot;2016-01-19 04:51:19&quot;,
&quot;sys_domain&quot;: {
&quot;link&quot;: &quot;https://instance.service-now.com/api/now/table/sys_user_group/global&quot;,
&quot;value&quot;: &quot;global&quot;
},
&quot;state&quot;: &quot;4&quot;,
&quot;sys_created_by&quot;: &quot;glide.maint&quot;,
&quot;knowledge&quot;: &quot;false&quot;,
&quot;order&quot;: &quot;&quot;,
&quot;closed_at&quot;: &quot;2016-01-19 04:52:04&quot;,
&quot;cmdb_ci&quot;: {
&quot;link&quot;: &quot;https://instance.service-now.com/api/now/table/cmdb_ci/55b35562c0a8010e01cff22378e0aea9&quot;,
&quot;value&quot;: &quot;55b35562c0a8010e01cff22378e0aea9&quot;
},
&quot;delivery_plan&quot;: &quot;&quot;,
&quot;impact&quot;: &quot;3&quot;,
&quot;active&quot;: &quot;false&quot;,
&quot;work_notes_list&quot;: &quot;&quot;,
&quot;business_service&quot;: &quot;&quot;,
&quot;priority&quot;: &quot;4&quot;,
&quot;sys_domain_path&quot;: &quot;/&quot;,
&quot;time_worked&quot;: &quot;&quot;,
&quot;expected_start&quot;: &quot;&quot;,
&quot;rejection_goto&quot;: &quot;&quot;,
&quot;opened_at&quot;: &quot;2016-01-19 04:49:47&quot;,
&quot;business_duration&quot;: &quot;1970-01-01 00:00:00&quot;,
&quot;group_list&quot;: &quot;&quot;,
&quot;work_end&quot;: &quot;&quot;,
&quot;approval_set&quot;: &quot;&quot;,
&quot;wf_activity&quot;: &quot;&quot;,
&quot;work_notes&quot;: &quot;&quot;,
&quot;short_description&quot;: &quot;Switch occasionally drops connections&quot;,
&quot;correlation_display&quot;: &quot;&quot;,
&quot;delivery_task&quot;: &quot;&quot;,
&quot;work_start&quot;: &quot;&quot;,
&quot;assignment_group&quot;: &quot;&quot;,
&quot;additional_assignee_list&quot;: &quot;&quot;,
&quot;description&quot;: &quot;Switch occasionally drops connections&quot;,
&quot;calendar_duration&quot;: &quot;1970-01-01 00:02:17&quot;,
&quot;close_notes&quot;: &quot;updated firmware&quot;,
&quot;sys_class_name&quot;: &quot;problem&quot;,
&quot;closed_by&quot;: &quot;&quot;,
&quot;follow_up&quot;: &quot;&quot;,
&quot;sys_id&quot;: &quot;04ce72c9c0a8016600b5b7f75ac67b5b&quot;,
&quot;contact_type&quot;: &quot;phone&quot;,
&quot;urgency&quot;: &quot;3&quot;,
&quot;company&quot;: &quot;&quot;,
&quot;reassignment_count&quot;: &quot;&quot;,
&quot;activity_due&quot;: &quot;&quot;,
&quot;assigned_to&quot;: &quot;&quot;,
&quot;comments&quot;: &quot;&quot;,
&quot;approval&quot;: &quot;not requested&quot;,
&quot;sla_due&quot;: &quot;&quot;,
&quot;comments_and_work_notes&quot;: &quot;&quot;,
&quot;due_date&quot;: &quot;&quot;,
&quot;sys_mod_count&quot;: &quot;1&quot;,
&quot;sys_tags&quot;: &quot;&quot;,
&quot;escalation&quot;: &quot;0&quot;,
&quot;upon_approval&quot;: &quot;proceed&quot;,
&quot;correlation_id&quot;: &quot;&quot;,
&quot;location&quot;: &quot;&quot;
}
]
}

result field contains array of elements, and that elements can be different depending from API consumed.

For more info about that API is Table API from ServiceNow Product.

As you can see on documentation, API path is something like this: GET /now/table/{tableName}, where tableName could be different values and result elements tipology will depend from table fetched.

So the response elements inside result field can be formed with different name or different quantity of attributes. Basically it's a database exposed through HTTP, so every table could be formed with different number of columns.

My GET Implementation:

public Mono&lt;Result&gt; get(){
return client.get()
.uri(uriBuilder -&gt; uriBuilder
.path(this.tablePath)
.queryParam(&quot;active&quot;, &quot;true&quot;)
.queryParam(&quot;state&quot;, &quot;1&quot;)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Result.class);
}

POJOs

@Data
public class ProjectTask {
private String project;
}
@Data
public class Result &lt;T&gt; {
List&lt;T&gt; result;
}

Simple Usage to get the error:

List&lt;ProjectTask&gt; projectTasks = Objects.requireNonNull(
client.table(&quot;pm_project_task&quot;)
.get().block())
.getResult();
for (ProjectTask task : projectTasks){
System.out.println(&quot;========PROJECT=============== &quot;+task.getProject());
}

My runtime error at line for (ProjectTask task : projectTasks){

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to my.namespace.api.services.servicenow.model.ProjectTask

Response Needs:

  • Result POJO has to use Generic Type to manage different response elements type.

  • If first point cannot be accomplished try to give a solution to fetch that API with a reusable GET implementation for different table types.

  • I don't want to return a List<ProjectTask> from my client, my idea is that this library only return Mono or Flux objects.

答案1

得分: 3

你需要使用parameterizedtypereference将 JSON 反序列化为Result的确切类型。否则,Spring会将任何未知类型反序列化为LinkedHashMap

例如:

  public Mono<Result<ProjectTask>> get(){
    return client.get()
        .uri(uriBuilder -> uriBuilder
            .path(this.tablePath)
            .queryParam("active", "true")
            .queryParam("state", "1")
            .build())
        .accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToMono(new ParameterizedTypeReference<Result<ProjectTask>>() {
        });
  }

如果你想使这个get方法成为通用方法,适用于所有类型的Result,那么你应该参考这个 StackOverflow 问题

英文:

You need to use parameterizedtypereference to deserialize the json to the exact type of Result. Else Spring deserializes any unknown type to LinkedHashMap.
eg.

  public Mono&lt;Result&lt;ProjectTask&gt;&gt; get(){
return client.get()
.uri(uriBuilder -&gt; uriBuilder
.path(this.tablePath)
.queryParam(&quot;active&quot;, &quot;true&quot;)
.queryParam(&quot;state&quot;, &quot;1&quot;)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(new ParameterizedTypeReference&lt;Result&lt;ProjectTask&gt;&gt;() {
});
}

And if you want to make this method get a generic one, that will work for all types of Result, then you should look at this stackoverflow question.

huangapple
  • 本文由 发表于 2020年7月25日 17:06:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/63086475.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定