Как сделать трансформацию данных?

В ответе с базы данных у меня приходит:

[Order{date='2017-10-16 12:07:07', docTypesName='Приход', docId=564564867361367, image='https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg', name='Молочный Изюм 100', price=102.0, quantity=45, removed=0}, Order{date='2017-10-16 12:07:07', docTypesName='Приход', docId=564564867361367, image='https://mariupolcena.com/files/products/9ff44136e6ccb0afb404ad26f727e67d.jpeg', name='Русская картошка чедар 50', price=46.3, quantity=45, removed=0}, Order{date='2017-10-16 12:07:07', docTypesName='Расход', docId=564564867361367, image='https://mariupolcena.com/files/products/9ff44136e6ccb0afb404ad26f727e67d.jpeg', name='Русская картошка чедар 50', price=46.3, quantity=45, removed=0}, Order{date='2017-11-29 17:26:57', docTypesName='Расход', docId=564564867361365, image='https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg', name='Молочный Изюм 100', price=102.0, quantity=6, removed=0}]

jackson-databind в Spring автоматически выполняет сериализацию. Его JSON представление:

[
    {
        "date": "2017-10-16 12:07:07",
        "docTypesName": "Приход",
        "docId": 564564867361367,
        "image": "https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg",
        "name": "Молочный Изюм 100",
        "price": 102,
        "quantity": 45,
        "removed": 0
    },
    {
        "date": "2017-10-16 12:07:07",
        "docTypesName": "Приход",
        "docId": 564564867361367,
        "image": "https://mariupolcena.com/files/products/9ff44136e6ccb0afb404ad26f727e67d.jpeg",
        "name": "Русская картошка чедар 50",
        "price": 46.3,
        "quantity": 45,
        "removed": 0
    },
    {
        "date": "2017-10-16 12:07:07",
        "docTypesName": "Расход",
        "docId": 564564867361367,
        "image": "https://mariupolcena.com/files/products/9ff44136e6ccb0afb404ad26f727e67d.jpeg",
        "name": "Русская картошка чедар 50",
        "price": 46.3,
        "quantity": 45,
        "removed": 0
    },
    {
        "date": "2017-11-29 17:26:57",
        "docTypesName": "Расход",
        "docId": 564564867361365,
        "image": "https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg",
        "name": "Молочный Изюм 100",
        "price": 102,
        "quantity": 6,
        "removed": 0
    },
]

Подскажите, пожалуйста, как правильно сделать преобразование данных, чтобы получить следующую структуру (JSON представление):

[
    {
        "date": "2017-10-16",
        documents: [
            {
                date: "2017-10-16 12:07:07",
                docId: "564564867361367",
                docTypesName: "Приход",
                products: [
                    {
                        "image": "https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg",
                        "name": "Молочный Изюм 100",
                        "price": 102,
                        "quantity": 45,
                    },
                    {
                        "image": "https://mariupolcena.com/files/products/9ff44136e6ccb0afb404ad26f727e67d.jpeg",
                        "name": "Русская картошка чедар 50",
                        "price": 46.3,
                        "quantity": 45,
                    }
                ]
            },
            {
                "date": "2017-10-16 12:07:07",
                "docId": 564564867361367,
                "docTypesName": 'Расход',
                "products": [
                    {
                        "image": "https://mariupolcena.com/files/products/9ff44136e6ccb0afb404ad26f727e67d.jpeg",
                        "name": "Русская картошка чедар 50",
                        "price": 46.3,
                        "quantity": 45,
                    }
                ]
            }
        ]
    },
    {
        "date": "2017-11-29",
        "documents": [
            {
                "date": "2017-11-29 17:26:57",
                "docId": 564564867361365,
                "docTypesName": 'Расход',
                "products": [
                    {
                        "image": "https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg",
                        "name": "Молочный Изюм 100",
                        "price": 102,
                        "quantity": 6,
                    }
                ]
            }
        ]
    }
]

Ниже я описал класс, который формирует исходную структуру данных: Order.java

package main.java;

import java.util.ArrayList;
import java.util.List;

public class Order {

    private String date;

    private String docTypesName;

    private String docId;

    private String image;

    private String name;

    private double price;

    private int quantity;

    private int removed;

    public Order(String date, String docTypesName, String docId, String image, String name, double price, int quantity, int removed) {
        this.date = date;
        this.docTypesName = docTypesName;
        this.docId = docId;
        this.image = image;
        this.name = name;
        this.price = price;
        this.quantity = quantity;
        this.removed = removed;
    }

    @Override
    public String toString() {
        return "Order{" +
                "date='" + date + '\'' +
                ", docTypesName='" + docTypesName + '\'' +
                ", docId=" + docId +
                ", image='" + image + '\'' +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", quantity=" + quantity +
                ", removed=" + removed +
                '}';
    }
}

class Test2 {
    public static void main(String[] args) {
        Order order = new Order("2017-10-16 12:07:07",
                "Приход",
                "564564867361367",
                "https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg",
                "Молочный Изюм 100",
                102,
                45,
                0);

        Order order2 = new Order("2017-10-16 12:07:07",
                "Приход",
                "564564867361367",
                "https://mariupolcena.com/files/products/9ff44136e6ccb0afb404ad26f727e67d.jpeg",
                "Русская картошка чедар 50",
                46.3,
                45,
                0);

        Order order3 = new Order("2017-10-16 12:07:07",
                "Расход",
                "564564867361367",
                "https://mariupolcena.com/files/products/9ff44136e6ccb0afb404ad26f727e67d.jpeg",
                "Русская картошка чедар 50",
                46.3,
                45,
                0);

        Order order4 = new Order("2017-11-29 17:26:57",
                "Расход",
                "564564867361365",
                "https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg",
                "Молочный Изюм 100",
                102,
                6,
                0);

        List<Order> orderList = new ArrayList<>();

        orderList.add(order);
        orderList.add(order2);
        orderList.add(order3);
        orderList.add(order4);

        System.out.println(orderList);
    }
}

Ответы (2 шт):

Автор решения: Alexandr

Вам необходимо ввести дополнительные три сущности, например, как в примере ниже - Day, Document, Item.

package com.somepackage;

import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@ToString
@Builder
@Getter
public class Order {

    private final LocalDateTime date;
    private final String docTypeName;
    private final String docId;
    private final String image;
    private final String name;
    private final double price;
    private final int quantity;
    private final boolean removed;

}

@ToString
@Builder
@Getter
class Item {

    private final String image;
    private final String name;
    private final Double price;
    private final Integer quantity;

}

@ToString
@Builder
@Getter
class Doc {

    private final LocalDateTime date;
    private final String docId;
    private final String docTypeName;
    private final List<Item> items;

}

@ToString
@Builder
@Getter
class Day {

    private final LocalDate date;
    private final List<Doc> documents;

}

class Test2 {
    public static void main(String[] args) {
        List<Order> orders =
                Stream.of(
                        Order.builder()
                                .date(LocalDateTime.of(2017, 10, 16, 12, 7, 7))
                                .docTypeName("Приход")
                                .docId("564564867361367")
                                .image("https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg")
                                .name("Молочный Изюм 100")
                                .price(102)
                                .quantity(45)
                                .removed(false)
                                .build(),
                        Order.builder()
                                .date(LocalDateTime.of(2017, 10, 16, 12, 7, 7))
                                .docTypeName("Приход")
                                .docId("564564867361367")
                                .image("https://mariupolcena.com/files/items/9ff44136e6ccb0afb404ad26f727e67d.jpeg")
                                .name("Русская картошка чедар 50")
                                .price(46.3)
                                .quantity(45)
                                .removed(false)
                                .build(),
                        Order.builder()
                                .date(LocalDateTime.of(2017, 10, 16, 12, 7, 7))
                                .docTypeName("Расход")
                                .docId("564564867361367")
                                .image("https://mariupolcena.com/files/items/9ff44136e6ccb0afb404ad26f727e67d.jpeg")
                                .name("Русская картошка чедар 50")
                                .price(46.3)
                                .quantity(45)
                                .removed(false)
                                .build(),
                        Order.builder()
                                .date(LocalDateTime.of(2017, 11, 29, 17, 26, 57))
                                .docTypeName("Расход")
                                .docId("564564867361365")
                                .image("https://www.komus.ru/medias/sys_master/root/hd3/h93/9286922043422.jpg")
                                .name("Молочный Изюм 100")
                                .price(102)
                                .quantity(6)
                                .removed(false)
                                .build()
                ).collect(Collectors.toList());

        List<Day> days = new ArrayList<>();
        for (Order order : orders) {
            LocalDate date =
                    order.getDate().toLocalDate();
            Day day =
                    days.stream()
                            .filter(d -> d.getDate().equals(date))
                            .findFirst()
                            .orElseGet(() -> {
                                Day newDay =
                                        Day.builder()
                                                .date(date)
                                                .documents(new ArrayList<>())
                                                .build();
                                days.add(newDay);
                                return newDay;
                            });

            String docId =
                    order.getDocId();
            String docTypeName =
                    order.getDocTypeName();
            Doc doc =
                    day.getDocuments().stream()
                            .filter(d ->
                                    d.getDocId().equals(docId) &&
                                            d.getDocTypeName().equals(docTypeName))
                            .findFirst()
                            .orElseGet(() -> {
                                Doc newDoc =
                                        Doc.builder()
                                                .date(order.getDate())
                                                .docId(docId)
                                                .docTypeName(docTypeName)
                                                .items(new ArrayList<>())
                                                .build();
                                day.getDocuments().add(newDoc);
                                return newDoc;
                            });

            String itemName =
                    order.getName();
            doc.getItems().stream()
                    .filter(p -> p.getName().equals(itemName))
                    .findFirst()
                    .ifPresentOrElse(
                            p -> {
                                throw new IllegalArgumentException();
                            },
                            () -> {
                                Item newItem =
                                        Item.builder()
                                                .name(itemName)
                                                .image(order.getImage())
                                                .quantity(order.getQuantity())
                                                .price(order.getPrice())
                                                .build();
                                doc.getItems().add(newItem);
                            });
        }

        System.out.println(days);
    }
}
→ Ссылка
Автор решения: Дмитрий

Я бы лучше прокинул через хешированную коллекцию по одной простой причине - читаемость кода. Это выглядит несоизмеримо проще:

    public static Collection<OrderTarget> convert(List<Order> orders) {
        return convert(orders.toArray(new Order[orders.size()]));
    }
    public static Collection<OrderTarget> convert(Order[] orders) {

        Map<Document, Document> documents = new HashMap<>();
        for (Order order : orders) {            
            Document doc = new Document(order);        
            if (documents.containsKey(doc)) documents.get(doc).addProduct(new Product(order));
            else documents.put(doc, doc.addProduct(new Product(order)));
        }
        
        Map<LocalDate ,OrderTarget> orderTargets = new HashMap<>();
        for (Document document : documents.values()) {            
            LocalDate date = document.getDate().toLocalDate();
            if (orderTargets.containsKey(date)) orderTargets.get(date).addDocument(document);
            else orderTargets.put(date, new OrderTarget(date, document));
        }
        
        return orderTargets.values();
    }   

@lombok.Data
@lombok.NoArgsConstructor
@lombok.AllArgsConstructor
public class Order {

    private String date;
    private String docTypesName;
    private String docId;
    private String image;
    private String name;
    private double price;
    private int quantity;
    private int removed;
}

@lombok.Data
@lombok.NoArgsConstructor
class OrderTarget {
    public LocalDate date;
    public List<Document> documents = new ArrayList<>();

    public OrderTarget(LocalDate date, Document document) {
        this.date = date;
        this.documents.add(document);
    }
    
    public OrderTarget addDocument(Document document) {
        documents.add(document);
        return this;
    }
    
}

@lombok.Data
@lombok.NoArgsConstructor
@lombok.EqualsAndHashCode(exclude = "products")
class Document {
    
    private final static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
    public LocalDateTime date;
    public String docId;
    public String docTypesName;
    public List<Product> products = new ArrayList<>();

    public Document(Order order) {
        this.date = LocalDateTime.parse(order.getDate(), FORMATTER);
        this.docId = order.getDocId();
        this.docTypesName = order.getDocTypesName();
    }
    
    public Document addProduct(Product product) {
        products.add(product);
        return this;
    }
    
}

@lombok.Data
@lombok.NoArgsConstructor
class Product {
    public String image;
    public String name;
    public double price;
    public int quantity;

    public Product(Order order) {
        this.image = order.getImage();
        this.name = order.getName();
        this.price = order.getPrice();
        this.quantity = order.getQuantity();
    }    
}
→ Ссылка