0

I have a sample JSON as follows. I have to map this object to another JSON format which is canonical to the UI (getting different orders from different vendors and aggregating them to a common UI format).

If I generate POJOs, it will create Order_1, Order_2... classes under the outer class which looks dirty. And during development time, I may not be able to anticipate how many orders may come during peak. So how do I approach this problem?

My end result should be able to map this JSON to the target JSON where repeatable elements are arrays.

{
    "TotalOrders": 6,
    "Order_1": {
        "Item_1": {
            "item": "Shirt",
            "Quantity": 2
        },
        "Item_2": {
            "item": "Jeans",
            "Quantity": 2
        }

    },
    "Order_2": {
        "Item_1": {
            "item": "Caps",
            "Quantity": 2
        },
        "Item_2": {
            "item": "Bags",
            "Quantity": 2
        },
        "Item_3": {
            "item": "Chains",
            "Quantity": 2
        }
    },
    "Order_3": {
        "Item_1": {
            "item": "Watches",
            "Quantity": 2
        },
        "Item_2": {
            "item": "Rings",
            "Quantity": 2
        },
        "Item_3": {
            "item": "Perfumes",
            "Quantity": 2
        },
        "Item_4": {
            "item": "Deo",
            "Quantity": 1
        }
    },
    "Order_4": {
        "Item_1": {
            "item": "Cans",
            "Quantity": 2
        },
        "Item_2": {
            "item": "Tubes",
            "Quantity": 2
        },
        "Item_3": {
            "item": "Tents",
            "Quantity": 2
        }
    },
    "Order_5": {
        "Item_1": {
            "item": "Butter",
            "Quantity": 2
        },
        "Item_2": {
            "item": "Jam",
            "Quantity": 2
        },
        "Item_3": {
            "item": "Bread",
            "Quantity": 2
        }
    },
    "Order_6": {
        "Item_1": {
            "item": "DVD",
            "Quantity": 2
        },
        "Item_2": {
            "item": "Floppy",
            "Quantity": 2
        },
        "Item_3": {
            "item": "Cables",
            "Quantity": 2
        }
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
user2729074
  • 3
  • 1
  • 5

1 Answers1

0

This can be solved using a custom deserializer. However, you will need to parse the json into Jackson's tree model and I find working with the tree model and Jackson's JsonNode very cumbersome and unintuitive.

So, an alternative is to use Jackson's anySetter feature which is an annotated method that will receive all "unknwown" properties. The advantage over custom deserializer is that you get the json already parsed into a Map, that is much easier to traverse and process.

Here is an example, which will parse any number of orders and items into POJO with arrays. I used arrays since these are more simple to work when the access is through indexes.

import java.io.*;
import java.util.*;
import java.util.stream.*;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;

public class OrderList
{
    private Order[] orders = null;
    @JsonProperty("TotalOrders")
    private int totalOrders = -1;

    public static class Order
    {
        private OrderedItem[] items = null;

        public OrderedItem[] getItems() { return items; }

        @Override
        public String toString()
        {
            return "Order:" + Arrays.toString(getItems());
        }
    }

    public static class OrderedItem
    {
        public String item;
        public int quantity;

        public OrderedItem() {}
        public OrderedItem(String item, int quantity)
        {
            setItem(item);
            setQuantity(quantity);
        }

        public String getItem() { return item; }
        public void setItem(String newItem) { item = newItem; }

        public int getQuantity() { return quantity; }
        public void setQuantity(int newQty) { quantity = newQty; }

        @Override
        public String toString()
        {
            return "OrderedItem:{" + getItem() + "," + getQuantity() + "}";
        }
    }

    // all properties exepct totalOrders will be directed here
    @SuppressWarnings("unchecked")
    @JsonAnySetter
    public void setOrders(String key, Object value)
    {
        // initialize orders array according to totalOrders
        if (orders == null  &&  totalOrders > 0) orders = new Order[totalOrders];
        // parse order idx from property name
        int parseOrderIdx = -1;
        if (key != null  &&  key.startsWith("Order_")) {
            parseOrderIdx = Integer.parseInt(key.split("_")[1]);
        }
        if (orders == null  ||  parseOrderIdx < 1  ||  parseOrderIdx > orders.length) {
            System.err.println("ERROR in parsing totalOrders and/or order idx");
            return;
        }

        // java requires final variable to be used in lambda expr.
        final int orderIdx = parseOrderIdx;
        orders[orderIdx-1] = new Order();
        // value arg is map of items
        Map<String, Object> items = (Map<String, Object>)value;
        orders[orderIdx-1].items = new OrderedItem[items.size()];
        IntStream.rangeClosed(1, items.size()).forEach(itemIdx -> {
            Map<String, Object> item = (Map<String, Object>)items.get("Item_" + itemIdx);
            if (item == null) {
                System.err.println("ERROR in parsing item Item_" + itemIdx + " order Order_" + orderIdx);
                return;
            }
            orders[orderIdx-1].items[itemIdx-1] =
                    new OrderedItem((String)item.get("item"), (Integer)item.get("Quantity"));
        });
    }

    public static void main(String[] args)
    {
        ObjectMapper mapper = new ObjectMapper();
        try (InputStream is = new FileInputStream("C://Temp/xx.json")){
            OrderList ol = mapper.readValue(is, OrderList.class);
            System.out.println(Arrays.toString(ol.orders));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Sharon Ben Asher
  • 13,849
  • 5
  • 33
  • 47