'Split data in JSON file based on a key value using java

I have a json file with which has an array of product items i want to split them based on the category of the item, This is my json file looks like,

{
    "items":[
        {
            "item-id": 123,
            "name": "Cheese",
            "price": 8,
            "category": "Dairy" 
        },
        {
            "item-id": 124,
            "name": "Milk",
            "price": 23,
            "category": "Dairy"
        },
        {
            "item-id": 125,
            "name": "Chicken",
            "price": 100,
            "category": "Meat"
        },
        {
            "item-id": 126,
            "name": "Fish",
            "price": 45,
            "category": "Meat"
        }
    ]
}

i want to split them like like this,

[
    {
        "category":"Dairy",
        "items":[
            {
                "item-id": 123,
                "name": "Cheese",
                "price": 8,
                "category": "Dairy" 
            },
            {
                "item-id": 124,
                "name": "Milk",
                "price": 23,
                "category": "Dairy"
            }
        ]
    },
    {
        "category":"Meat",
        "items":[
            {
                "item-id": 125,
                "name": "Chicken",
                "price": 100,
                "category": "Meat"
            },
            {
                "item-id": 126,
                "name": "Fish",
                "price": 45,
                "category": "Meat"
            }
        ]
    }
]

this is the code i tried so far but can't find way to split them like the way i wanted, I'm using java and also i am new to java

import java.io.*; 
import java.util.*;
import org.json.simple.*;
import org.json.simple.parser.*;

public class ReadOrderDetails {

    @SuppressWarnings("unused")
    public static void main(String[] args) {
        JSONParser parser = new JSONParser();
        JSONObject subOrder = new JSONObject();
        JSONArray  gitems = new JSONArray();
        JSONArray  subOrders = new JSONArray();
        
        try {
            Object obj = parser.parse(new FileReader("order-details.json"));
            JSONObject jsonObject = (JSONObject)obj;
            String orderId = (String)jsonObject.get("orderId");
            JSONArray items = (JSONArray)jsonObject.get("items");
            @SuppressWarnings("rawtypes")
            Iterator iterator = items.iterator();
            System.out.println("Order Id: " + orderId);
            while(iterator.hasNext()) {
                JSONObject item = (JSONObject)iterator.next();
                if(subOrders.isEmpty()) {
                    subOrder.put("category", item.get("category"));
                    gitems.add(item);
                    subOrder.put("items", gitems);
                    subOrders.add(subOrder);
                } else {
                    Iterator subOrdersIterator = subOrders.iterator();
                    for(int i=0; i<subOrders.size(); i++) {
                        JSONObject sitem = (JSONObject) subOrdersIterator.next();
                        if(sitem.get("category") == item.get("category")) {
                            gitems.add(item);
                            subOrder.put("items", gitems);
                            subOrders.add(subOrder);
                        } else {
                            subOrder.put("category", item.get("category"));
                            gitems.add(item);
                            subOrder.put("items", gitems);
                            subOrders.add(subOrder);
                        }
                    }
                }
                
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
        System.out.println(subOrders);
    }

}



and also i'm getting an error at java.util.ConcurrentModificationException but that's not my main question, what i really wnated a way to split them i tried couple of things didn't working



Solution 1:[1]

In general, you violated the Single Responsibility principal (SOLID), because your code do two different things together: parse a file and categorize items. You should split these responsibilities.

One of ways how you do this is to create classes that represents your data. Let's assume that these classes are Item, Category, Order and CategorizedItems. Order is a class that represents your source JSONObject and CategorizedItems - represents your result JSONArray.

In this case, you should firstly parse the file into these classes and only after that transform them into JSONArray.

Code sample:

Data classes:

class Order {
    private final List<Item> items = new ArrayList<>();

    public void addItem(Item item) {
        items.add(item);
    }

    public List<Item> getAllItems() {
        return new ArrayList<>(items);
    }
}

enum Category {
    DAIRY("Dairy"),
    MEAT("Meat");

    private final String value;

    Category(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public static Category of(String value) {
        for (Category category : values()) {
            if (category.value.equals(value)) {
                return category;
            }
        }

        throw new IllegalArgumentException("Unknown category");
    }
}

class CategorizedItems {
    private final Category category;
    private final List<Item> items;

    public CategorizedItems(Category category, List<Item> items) {
        this.category = category;
        this.items = items;
    }

    public Category getCategory() {
        return category;
    }

    public List<Item> getItems() {
        return items;
    }
}
class Item {
    private final long id;
    private final String name;
    private final long price;
    private final Category category;


    public Item(long id, String name, long price, Category category) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.category = category;
    }

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public long getPrice() {
        return price;
    }

    public Category getCategory() {
        return category;
    }
}

Now you should write methods, for example, static ones in your ReadOrderDetails:

    private static JSONArray categorizedItemsToJSONArray(List<CategorizedItems> categorizedItems) {
        JSONArray jsonArray = new JSONArray();
        categorizedItems.forEach(category -> jsonArray.add(toJSONObject(category)));

        return jsonArray;
    }

    private static JSONObject toJSONObject(CategorizedItems categorizedItems) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("category", categorizedItems.getCategory().getValue());
        jsonObject.put("items", itemsToJSONArray(categorizedItems.getItems()));

        return jsonObject;
    }

    private static JSONArray itemsToJSONArray(List<Item> items) {
        JSONArray jsonArray = new JSONArray();
        items.forEach(item -> jsonArray.add(toJSONObject(item)));

        return jsonArray;
    }

    private static JSONObject toJSONObject(Item item) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("item-id", item.getId());
        jsonObject.put("name", item.getName());
        jsonObject.put("price", item.getName());
        jsonObject.put("category", item.getCategory().getValue());

        return jsonObject;
    }


    private static List<CategorizedItems> categorize(Order order) {
        List<CategorizedItems> categorizedItems = new ArrayList<>();
        for (Category category : Category.values()) {
            categorizedItems.add(
                    new CategorizedItems(
                            category,
                            order.getAllItems()
                                    .stream()
                                    .filter(item -> item.getCategory() == category)
                                    .collect(Collectors.toList())
                    )
            );
        }

        return categorizedItems;
    }

    private static Order orderFrom(JSONObject jsonObject) {
        JSONArray itemsJsonArray = (JSONArray) jsonObject.get("items");

        Order order = new Order();
        for (Object jsonArrayMember : itemsJsonArray) {
            JSONObject itemJsonObject = (JSONObject) jsonArrayMember;
            order.addItem(itemFrom(itemJsonObject));
        }

        return order;
    }

    private static Item itemFrom(JSONObject jsonObject) {
        long itemId = (Long) jsonObject.get("item-id");
        String itemName = (String) jsonObject.get("name");
        long itemPrice = (Long) jsonObject.get("price");
        Category category = Category.of((String) jsonObject.get("category"));

        return new Item(
                itemId,
                itemName,
                itemPrice,
                category
        );
    }

After that you should do the last step. Write a main function like:

    public static void main(String[] args) {
        JSONParser parser = new JSONParser();


        try {
            JSONObject orderJsonObject = (JSONObject) parser.parse(new FileReader("order-details.json"));
            Order order = orderFrom(orderJsonObject);
            List<CategorizedItems> categorizedItems = categorize(order);
            JSONArray categorizedItemsJsonArray = categorizedItemsToJSONArray(categorizedItems);

            // write categorizedItemsJsonArray to a file here
        } catch(IOException | ParseException e) {
            throw new RuntimeException(e);
        }
    }

Now your code is simply readable and clear. You can easily maintain it. I advise you to read about SOLID principals and about code style (I highly recommend Robert Martin. "Clean code"). These topics will help you learn how to write elegant and clear code.

Besides, ConcurrentModificationException is a sign that you do something wrong with collections. In your particular case, I guess (not sure) the problem is modifying the JSONArray while you are iteratting it. Only some of Java collections allow it.

EDIT Fix problem with integers by replacing them with long.

Good luck with Java :)

Solution 2:[2]

You can't alter a collection while iterating it. Try this:

                Object[] subOrderArray = subOrders.toArray();
                for(Object o: subOrderArray) {
                    JSONObject sitem = (JSONObject) o;

Solution 3:[3]

Here you want to group the JSON data by category under items array. The code will be very long if you try to get this done in Java. Just try using SPL, an open-source Java package, to do this. You only need one line of code:

A
1 =json(file("data.json").read()).items.group(#4;~:items)

SPL offers JDBC driver to be invoked by Java. Just store the above SPL script as group.splx and invoke it in a Java application as you call a stored procedure:

…
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
st = con.prepareCall("call group()");
st.execute();
…

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Maurice Perry
Solution 3 LeoTaylor