#java #json
#java #json
Вопрос:
Я нашел этот образец JSON для электронной коммерции. Я хотел упорядочить этот JSON иерархически.
Вы можете найти этот файл JSON по приведенному ниже URL.
https://stark-spire-93433.herokuapp.com/json
Я хотел упорядочить этот JSON следующим образом.
Mens Wear
|Bottom Wear
|Jeans
|Tracks amp; Trousers
|Foot Wear
|Casuals
|Formals
|Upper Wear
|Shirts
|T-Shirts
Electronics
|Mobiles
|Apple
|Samsung
|Laptops
|Dell
|Toshiba
Все варианты будут соответствовать последней иерархии продуктов. Например, Dell, Apple, Shirts и так далее.
Я создал классы моделей для этого JSON.
public class Data {
public ArrayList<Category> categories;
public ArrayList<Rank> rankings;
}
public class Category {
public int id;
public String name;
public ArrayList<Product> products;
public ArrayList<Integer> child_categories;
}
public class Rank {
public String ranking;
public ArrayList<Product> products;
}
public class Product {
public int id;
public String name;
public String date_added;
public ArrayList<Variant> variants;
public Tax tax;
public int view_count;
public int order_count;
public int shares;
}
public class Tax {
public String name;
public double value;
}
public class Variant {
public int id;
public String color;
//in some variants size is null eg. mobiles, if null pass 0.
public int size;
public double price;
}
Теперь я не понимаю, с чего мне начать.
Комментарии:
1. Я бы посоветовал не использовать никакого сопоставления объектов. Я бы подготовил несколько классов, чтобы представить древовидную структуру, которую вы хотите, например, если вам нужно будет построить дерево программно. Например, ваша категория должна содержать не ArrayList<Целое число> дочерние категории, а ArrayList<Категория> дочерние элементы… После этого используйте потоковый синтаксический анализ baeldung.com/jackson-streaming-api и постройте свое дерево, проходящее через токены JSON, как вам нужно.
Ответ №1:
Вот мое решение
Схема
- измените модель класса из древовидной структуры после добавления, используя составной шаблон проектирования
- десериализуйте json в модель класса из post, используя библиотеку Jackson.
- создайте
Map<Integer, Category>
, который обеспечивает прямой доступ к категории по ее идентификатору - выполните итерацию по категориям, для каждой из них выполните итерацию по ее дочерним элементам, для каждого дочернего идентификатора получите дочернюю категорию с карты и назначьте в качестве дочернего экземпляра
- наконец, выполните итерацию по дереву, используя поиск в глубину, и распечатайте категории, используя шаблон посетителя
Структура данных
Я взял вашу модель и немного изменил:
- Изменены списки, которые будут определены как интерфейс. это связано с тем, что построение переменной выполняется десериализатором json, который может решить использовать другую реализацию
List
- Добавлены коллекции для реализации составного шаблона: каждая
Category
имеет список дочерних категорий в качествеCategory
экземпляров,Data
добавлен список корней дерева. - Изменены имена переменных, чтобы лучше отражать назначение и использование.
Вот как выглядят измененные классы Data
и Category
:
public class Category
{
public int id;
public String name;
public List<Product> products;
@JsonProperty("child_categories")
public List<Integer> childCategoryIds;
// tree-structure properties
public boolean isRoot = true;
public List<Category> childCategories = new ArrayList<>();
public void visit(Visitor<Category> visitor, int level) {
visitor.accpet(this, level);
childCategories.forEach(cat -> cat.visit(visitor, level 1));
}
}
public class Data
{
public List<Category> categories;
public List<Rank> rankings;
// tree-structure properties
public List<Category> tree;
// initiate traversal of each root withj printer visitor
public void printTree() {
Visitor<Category> printer = new Printer();
tree.forEach(root -> root.visit(printer, 0));
}
}
Реализация решения
Шаблон посетителя
// visitor interface
public interface Visitor<T>
{
/**
* @param level 0 is root, 1 root's child and so on
*/
public void accpet(T node, int level);
}
// printer visitor: prints to console each visited category, properly indented
public class Printer implements Visitor<Category>
{
@Override
public void accpet(Category node, int level) {
System.out.println(indentByLevel(level) node.id " " node.name);
// in case of category with products - print them and variants
if (node.products != null amp;amp; !node.products.isEmpty()) {
printProducts(node, level);
}
}
private void printProducts(Category node, int level) {
node.products.forEach(product -> {
System.out.println(indentByLevel(level 1) product.id " " product.name);
if (product.variants != null amp;amp; !product.variants.isEmpty()) {
product.variants.forEach(variant -> {
System.out.println(indentByLevel(level 2) variant.id " " variant.color);
});
}
});
}
private static final String levelIndent = " ";
private String indentByLevel(int level) {
if (level > 0) {
return String.join("", Collections.nCopies(level, levelIndent));
}
return "";
}
}
Основной класс
public class JsonToTree
{
public static void main(String[] args) {
try {
Data data = readJson("https://stark-spire-93433.herokuapp.com/json");
jsonToTree(data);
data.printTree();
} catch (Exception e) {
e.printStackTrace();
}
}
// read and parse source to Data object
// using java 11 HttpURLConnection and Jackson ObjectMapper
public static Data readJson(String url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
try (InputStream responseStream = connection.getInputStream()) {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(responseStream, Data.class);
}
}
public static void jsonToTree(Data data) {
// populate array of categories. array index is category id
// this will allow random access to category by its id
Map<Integer, Category> categoryById = data.categories.stream()
.collect(Collectors.toMap(cat -> cat.id, Function.identity(), (cat1, cat2) -> cat1));
// populate list of category objects for each category according to list of child ids
// along the way, mark each child category as not root
data.categories.stream()
.filter(cat -> cat.childCategoryIds != null amp;amp; !cat.childCategoryIds.isEmpty())
.forEach(catWithChildren ->
catWithChildren.childCategoryIds.forEach(id -> {
Category child = categoryById.get(id);
if (child != null) {
catWithChildren.childCategories.add(child);
child.isRoot = false;
}
})
);
// build tree in data from root categories
data.tree = new ArrayList<>();
data.categories.stream()
.filter(cat -> cat.isRoot)
.forEach(root -> data.tree.add(root));
}
}
Распечатка
3 Mens Wear
4 Bottom Wear
2 Jeans
3 Spykar Denim
9 Blue
10 Black
11 Blue
12 Blue
4 Lee Cotton Jeans
13 Blue
14 Black
15 White
16 Black
24 Denim Wash
71 Blue
72 Grey
25 Pepe Jeans Slim Fit
73 Blue
74 Light Blue
26 Spykar Funky Regular
75 Blue
76 Black
8 Tracks amp; Trousers
7 Comfort Tracks
25 Blue
26 Red
27 White
28 Red
8 Adidas Trousers
29 White
30 Yellow
31 Green
32 Red
30 Superdry track
83 Red
84 Blue
31 Night Comfy Track
85 Red
86 Black
32 Superdry Joggers
87 Red
88 Blue
5 Foot Wear
1 Casuals
1 Nike Sneakers
1 Blue
2 Red
3 Blue
4 Red
2 Adidas Running Shoes
5 White
6 Black
7 White
8 Red
21 Roadster Loafers
65 Black
66 Blue
22 Light Loafers
67 Blue
68 Yellow
23 Floaters
69 Black
70 Red
9 Formals
9 Bata Lace up Shoes
33 Black
34 Brown
35 Black
36 Brown
10 Franco Leather
37 Black
38 Brown
39 Black
40 Brown
6 Upper Wear
7 T-Shirts
5 Polo Collar T-Shirt
17 Blue
18 Red
19 White
20 Red
6 Adidas Nylon
21 White
22 Yellow
23 Green
24 Red
27 Being Human Collar T-shirt
77 Blue
78 Black
28 V - Neck Smart T-Shirt
79 Blue
80 Black
29 Manchester United
81 Red
82 Red
10 Shirts
11 Wrangler Checked Shirt
41 Blue
42 Red
43 Black
44 White
12 Printed Shirt
45 Blue
46 Black
47 Red
48 Brown
11 Electronics
12 Mobiles
14 Apple
13 Iphone 6S
49 Silver
50 Golden
14 Iphone 7
51 Black
52 Silver
33 Iphone 6
89 Silver
90 Golden
34 Iphone 6s Plus
91 Silver
92 Golden
35 Iphone 7 Plus
93 Black
94 Grey
15 Samsung
15 Galaxy S7 Edge
53 Black
54 White
16 Galaxy J5
55 Black
56 White
36 Galaxy J7
95 Black
96 White
37 Galaxy Grand Prime
97 Black
98 White
38 Note 4
99 Black
100 White
13 Laptops
16 Dell
17 Dell Inspiron Core
57 Black
58 Red
18 Dell Inspiron 11
59 Black
60 Red
17 Toshiba
19 Satellite Pro
61 Black
62 Red
20 Satellite P50
63 Black
64 Red
Ответ №2:
Ну, проблема заключается в использовании косвенных ссылок на дочерние категории с использованием идентификаторов. Целью было бы преобразовать косвенные ссылки в прямые ссылки, такие как data.getCategory().getChildCategory().getName()
возможно.
Я бы решил это, введя два уровня абстракции:
Первый существует только для десериализации. Классы — давайте назовем их JsonBean
s — этого уровня будут очень похожи на структуру json, поэтому дочерние категории — это только числа. Это также имеет то преимущество, что JsonBean
s могут содержать специальные аннотации для десериализации, как они используются Jackson
.
Вторым слоем будет граф объектов, который используется в приложении, и он заменяет дочерние категории конкретными объектами. Давайте просто назовем этот тип классов Bean
. Bean
s будут независимы от дизайна JSON таким образом, что их можно будет использовать повторно, даже если данные загружаются из базы данных вместо JSON.
Затем процесс десериализации будет состоять из двух этапов: сначала необработанная десериализация в JsonBean
s. Затем следует служба преобразования, которая будет копировать JsonBean
файлы в приложения Bean
. Затем это преобразование преобразует идентификаторы дочерних категорий в конкретные Category
объекты.
Разделение между моделью чистого значения и моделью сохранения также обеспечивает некоторую безопасность на случай, если структура JSON будет изменена в будущем. Пока изменение не слишком серьезное, это мало повлияет на ваше приложение, поскольку вам нужно будет изменить только JsonBean
s и преобразование, а не все остальное, что использует данные после десериализации.