我正在使用 webclient 呼叫端點,并希望將我得到的回應映射到另一個物件。在映射該物件時,我想對回應的某些引數進行額外呼叫。
我的第一次呼叫回傳以下物件
{
"type": "Collection",
"key": "some.key",
"settings": [
{
"type": "Struct",
"key": "steps",
"value": [
{
"type": "Struct",
"key": "1",
"value": [
{
"type": "String",
"key": "headline",
"value": "someheadline"
},
{
"type": "String",
"key": "subheadline",
"value": "somesubheadline"
},
{
"type": "Link",
"key": "link.to.another.object",
"value": {
"linkType": "Boilerplate",
"key": "configurabletextkey"
}
}
]
}
]
},
{
"type": "Struct",
"key": "commons",
"value": [
{
"type": "String",
"key": "mandatory.fields.text",
"value": "Pflichtfelder"
}
]
}
]
}
我像這樣映射該回應:
webClient
.get()
.uri(
uriBuilder ->
uriBuilder
.path(headlessConfig.getEndpoint())
.pathSegment(contentId)
.build())
.retrieve()
.bodyToMono(Collection.class)
.map(response -> {
return getCollectionContent(response);
})
在 getCollectionContent 方法中,我遍歷設定陣列并從回應中提取資料并將其映射到我的 PageContent 物件。
public class PageContent {
private String pageId;
private List<Message> messages;
}
public class Message {
@NonNull private String key;
@NonNull private String text;
private Boolean containsHtml = false;
}
如果回應包含“字串”型別,我只需將資料添加到訊息物件并將其添加到 PageContent 的串列中。
現在來解決問題。如果型別是“鏈接”,我想像上面一樣使用 webclient 對同一個端點進行另一個呼叫,以獲取該物件的密鑰和文本,從中創建一個訊息物件并將其添加到我現有的串列中。
對應的代碼如下所示:
webClient
.get()
.uri(
uriBuilder ->
uriBuilder
.path(headlessConfig.getEndpoint())
.pathSegment(contentKey)
.build())
.retrieve()
.bodyToMono(ConfigurableText.class)
.map(
configurableTextResponse -> {
messages.add(
new Message(
prefix configurableTextResponse.getKey(),
configurableTextResponse.getText(),
true));
return Mono.empty();
})
Now when I try to do that nothing happens and I just receive the PageContent object without the message for the link.
In a blocking way with resttemplate this logic should work but I would like to get it to work with webclient.
Edit:
Code to iterate through the list and extract the message data:
private PageContent getCollectionContent(Collection response) {
PageContent pageContent = new PageContent();
pageContent.setPageId(response.getKey());
List<Message> messages = new ArrayList<>();
response
.getSettings()
.forEach(
settingsItemsArray -> {
var settingsItemList = (List<?>) settingsItemsArray.getValue();
String prefix = settingsItemsArray.getKey() ".";
extractMessageText(prefix, (LinkedHashMap<?, ?>) settingsItemList.get(0), messages);
});
pageContent.setMessages(messages);
return pageContent;
}
Code to extract the MessageText, iterate further or get the missing text for a link type.
private void extractMessageText(
String prefix, LinkedHashMap<?, ?> settingsItem, List<Message> messages) {
String itemKey = (String) settingsItem.get(KEY);
String itemType = (String) settingsItem.get(TYPE);
switch (itemType) {
case "String":
messages.add(new Message(prefix itemKey, (String) settingsItem.get(VALUE)));
break;
case "Struct":
((List<?>) settingsItem.get(VALUE))
.forEach(
structItems ->
extractMessageText(
prefix settingsItem.get(KEY) ".",
(LinkedHashMap<?, ?>) structItems,
messages));
break;
case "Link":
webClient
.get()
.uri(
uriBuilder ->
uriBuilder
.path(headlessConfig.getEndpoint())
.pathSegment(contentKey)
.build())
.retrieve()
.bodyToMono(ConfigurableText.class)
.map(
configurableTextResponse -> {
messages.add(
new Message(
prefix configurableTextResponse.getKey(),
configurableTextResponse.getText(),
true));
return Mono.empty();
})
break;
default:
break;
}
}
uj5u.com熱心網友回復:
我已經更改了您的一些代碼,以使其與反應器模式更兼容。我已將遞回更改為expandDeep并使用 Jackson 來決議 JSON。我希望這會給你一些想法來解決你的問題。
List<Message> messages = Flux
.fromIterable(jsonNode.get("settings"))
//expand the graph into a stream of flat data and track the address of the node with 'prefix'
//expand/exapndDeep operators are alternatives of recursion in project reactor
.expandDeep(parent -> {
String parentPrefix = Optional.ofNullable(parent.get("prefix")).map(JsonNode::asText)
.orElse(parent.get("key").asText());
String type = parent.get("type").asText();
if (type.equals("Struct")) {
return Flux.fromIterable(parent.get("value"))
.cast(ObjectNode.class)
.map(child -> child.put("prefix", parentPrefix ":" child.get("key").asText()));
}
return Mono.empty();
})
//we have to choose only leaf nodes aka String and Link nodes
.filter(node -> Arrays.asList("String", "Link").contains(node.get("type").asText()))
//now process expanded leaf nodes
.flatMap(leaf -> {
if ("String".equals(leaf.get("type").asText())) {
return Mono.just(new Message(leaf.get("prefix").asText(), leaf.get("value").asText(), true));
}
if ("Link".equals(leaf.get("type").asText())) {
return webClient
.get()
.uri(
uriBuilder ->
uriBuilder
.pathSegment(leaf.get("key").asText())
.build())
.retrieve()
.bodyToMono(JsonNode.class)
.map(configurableTextResponse -> new Message(
leaf.get("prefix") configurableTextResponse.get("key").asText(),
configurableTextResponse.get("text").asText(),
true));
}
return Mono.empty();
})
// at this point we are getting stream of the Message objects from the Link/String nodes
//collect them into a list
.collectList()
//we have to subscribe()/block() the mono to actually invoke the pipline.
.block();
您的代碼什么也沒做的主要原因是您沒有訂閱 WebClient 管道。
編輯:
改變
.map(response -> {
return getCollectionContent(response);
})
到
.flatMap(response -> {
return getCollectionContent(response);
})
并從getCollectionContent(response) Mono<PageContent> page
就像是:
// at this point we are getting stream of the Message objects from the Link/String nodes
//collect them into a list
.collectList()
.map(messages -> {
PageContent pageContent = new PageContent();
pageContent.setPageId(response.get("pageId").asText());
pageContent.setMessages(messages);
return pageContent;
});
在這些更改之后,您getCollectionContent()將回傳一個發布 Mono<PageContent>者,該發布者將從 flatMap 運營商處訂閱。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/422674.html
標籤:
上一篇:SpringBoot:找不到帶有@Servicebeanbean的類
下一篇:在IntegrationFlow中使用DirectChannel的SpringIntegration拋出“Dispatcher沒有訂閱者”
