Элементы JSON с нумерацией как частью имени элемента
У меня есть образец JSON следующим образом. Я должен сопоставить этот объект с другим форматом JSON, каноническим для пользовательского интерфейса (получение разных заказов от разных поставщиков и объединение их в общий формат пользовательского интерфейса).
Если я сгенерирую POJO, он создаст классы Order_1, Order_2... под внешним классом, который выглядит грязным. А во время разработки я не могу предвидеть, сколько заказов может прийти в пиковый период. Так как мне подойти к этой проблеме?
Мой конечный результат должен быть в состоянии сопоставить этот JSON с целевым JSON, где повторяемые элементы являются массивами.
{
"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
}
}
}
1 ответ
Это можно решить с помощью специального десериализатора. Тем не менее, вам нужно будет проанализировать JSON в модели дерева Джексона, и я нахожу работу с моделью дерева и Джексона JsonNode
очень громоздко и не интуитивно понятно
Таким образом, альтернативой является использование функции Джексона anySetter, которая представляет собой аннотированный метод, который получит все "неизвестные" свойства. Преимущество по сравнению с пользовательским десериализатором заключается в том, что вы уже разобрали json в Map
, это намного легче пройти и обработать.
Вот пример, который будет анализировать любое количество заказов и предметов в POJO с массивами. Я использовал массивы, так как с ними проще работать, когда доступ осуществляется через индексы.
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();
}
}
}