У меня есть документ JSON, который является результатом анализа нескольких файлов:
{
"offer": {
"clientName": "Tom",
"insuranceCompany": "INSURANCE",
"address": "GAMLE BONDALSVEGEN 53",
"renewalDate": "22.12.2018",
"startDate": "22.12.2017",
"too_old": false,
"products": [
{
"productType": "TRAVEL",
"objectName": "Reiseforsikring - Holen, Tom Andre",
"name": null,
"value": null,
"isExclude": false,
"monthPrice": null,
"yearPrice": 1637,
"properties": {}
}
]
},
"documents": [
{
"clientName": "Tom",
"insuranceCompany": "INSURANCE",
"fileName": "insurance_tom.pdf",
"address": "GAMLE BONDALSVEGEN 53",
"renewalDate": "22.12.2019",
"startDate": "22.12.2018",
"issuedDate": "20.11.2018",
"policyNumber": "6497777",
"products": [
{
"productType": "TRAVEL",
"objectName": "Reiseforsikring - Holen, Tom Andre",
"name": null,
"value": null,
"isExclude": false,
"monthPrice": null,
"yearPrice": 1921,
"properties": {
"TRAVEL_PRODUCT_NAME": "Reise Ekstra",
"TRAVEL_DURATION_TYPE": "DAYS",
"TRAVEL_TYPE": "FAMILY",
"TRAVEL_DURATION": "70",
"TRAVEL_INSURED_CLIENT_NAME": "Holen, Tom Andre, Familie"
}
},
Я хочу перебрать все products
из раздела documents
и установить пропущенный properties
в products
из offer
section.
Предложение и документы на том же уровне глубины в JSON.
Для этого в Stream API реализовано следующее:
private void mergePropertiesToOffer(InsuranceDocumentsSession insuranceSession) {
Validate.notNull(insuranceSession, "insurance session can't be null");
if (insuranceSession.getOffer() == null) return;
log.info("BEFORE_MERGE");
// merge all properties by `objectName`
Stream.of(insuranceSession).forEach(session -> session.getDocuments().stream()
.filter(Objects::nonNull)
.flatMap(doc -> doc.getProducts().stream())
.filter(Objects::nonNull)
.filter(docProduct -> StringUtils.isNotEmpty(docProduct.getObjectName()))
.filter(docProduct -> MapUtils.isNotEmpty(docProduct.getProperties()))
.forEach(docProduct -> Stream.of(session.getOffer())
.flatMap(offer -> offer.getProducts().stream())
.filter(Objects::nonNull)
.filter(offerProduct -> MapUtils.isEmpty(offerProduct.getProperties()))
.filter(offerProduct -> StringUtils.isNotEmpty(offerProduct.getObjectName()))
.filter(offerProduct -> offerProduct.getObjectName().equals(docProduct.getObjectName()))
.forEach(offerProduct -> {
try {
ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
log.info("BEFORE_PRODUCT: {}", mapper.writeValueAsString(offerProduct));
offerProduct.setProperties(docProduct.getProperties());
log.info("UPDATED_PRODUCT: {}", mapper.writeValueAsString(offerProduct));
} catch (JsonProcessingException e) {
log.error("Error converting product to offer: {}", e.getCause());
}
})));
log.info("AFTER_MERGE");
}
Itработает отлично.Однако внедрение намного быстрее, чем поддержание в будущем.
Там два раза я использую фабричный метод Stream.of()
для получения потока для двух сущностей на другом уровне.Кроме того, flatMap()
используется в максимально возможной степени, + все нулевые проверки.
И вопрос не слишком ли сложен в этой реализации?
Должен ли он быть переработан и разделен на более мелкие части?Если да, как это должно быть с хорошими принципами программирования?
РЕШЕНИЕ:
Огромное спасибо nullpointer
ответ.
Окончательное решение следующее:
Map<Integer, InsuranceProductDto> offerProductMap = session.getOffer().getProducts()
.stream()
.filter(this::validateOfferProduct)
.collect(Collectors.toMap(InsuranceProductDto::getYearPrice, Function.identity(), (first, second) -> first));
Map<Integer, InsuranceProductDto> documentsProductMap = session.getDocuments()
.stream()
.flatMap(d -> d.getProducts().stream())
.filter(this::validateDocumentProduct)
.collect(Collectors.toMap(InsuranceProductDto::getYearPrice, Function.identity(), (first, second) -> first));
documentsProductMap.forEach((docPrice, docProduct) -> {
if (offerProductMap.containsKey(docPrice)) {
offerProductMap.compute(docPrice, (s, offerProduct) -> {
setProductProperties(offerProduct, docProduct);
return offerProduct;
});
}
});
// after finishing execution `offerProductMap` contains updated products