英文:
Find top category parent and add child below respective parent in sample JSON using java
问题
我在下面的URL上找到了这个样本电子商务JSON。我想按层次对这个JSON进行整理。
你可以在以下URL上找到这个JSON文件。
https://stark-spire-93433.herokuapp.com/json
我想按以下方式对这个JSON进行排列。
男装
|下装
|牛仔裤
|运动裤和长裤
|鞋类
|休闲鞋
|正装鞋
|上装
|衬衫
|T恤
电子产品
|手机
|苹果
|三星
|笔记本电脑
|戴尔
|东芝
所有变体都将位于产品的最后层次下。例如Dell、Apple、衬衫等。
我已经为这个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;
//对于某些变体,大小为null,例如手机,如果为null,则传递0。
public int size;
public double price;
}
现在我不知道该如何开始。
英文:
I found this sample eCommerce JSON. I wanted to arrange this JSON in hierarchically.
You can find this JSON file on below URL.
https://stark-spire-93433.herokuapp.com/json
I wanted to arrange this JSON in the following manner.
Mens Wear
|Bottom Wear
|Jeans
|Tracks & Trousers
|Foot Wear
|Casuals
|Formals
|Upper Wear
|Shirts
|T-Shirts
Electronics
|Mobiles
|Apple
|Samsung
|Laptops
|Dell
|Toshiba
All variants will go under the last hierarchy of products. For example Dell, Apple, Shirts and so on.
I have created model classes for this 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;
}
Now I don't understand how should I start.
答案1
得分: 0
问题在于使用间接引用通过ID引用子类别。目标是将这些间接引用转换为直接引用,类似于 data.getCategory().getChildCategory().getName()
这样的形式。
我会通过引入两个抽象层来解决这个问题:
第一个层仅用于反序列化。我们称这些类为 JsonBean
,它们的结构与JSON的结构非常相似,所以子类别只是数字。这还有一个优点,就是 JsonBean
可以包含用于反序列化的特殊注解,就像 Jackson
所使用的那样。
第二个层将是应用程序中使用的对象图,它会用具体的对象替换子类别。我们简单地将这种类型的类称为 Bean
。这些 Bean
与JSON的设计无关,这样即使从数据库加载数据而不是JSON,它们也可以被重用。
反序列化过程将分为两个步骤:首先是原始的反序列化为 JsonBean
。然后是一个转换服务,将 JsonBean
复制到应用程序的 Bean
中。这个转换将会将子类别的ID解析为具体的 Category
对象。
纯值模型和持久化模型之间的分离也可以在JSON的结构将来发生变化时提供一些安全性。只要变化不是太大,这对于你的应用程序影响较小,因为你只需要更改 JsonBean
和转换逻辑,而不必更改反序列化后使用数据的所有其他内容。
英文:
Well, the problem is the usage of indirect references to child-categories using ids. The goal would be to transform the indirect references to direct references such as something like data.getCategory().getChildCategory().getName()
is possible.
I would solve this by introducing two abstraction layers:
The first one exists only for deserialization. The classes - let's call them JsonBean
s - of this layer would closely resemble the structure of the json, so the child-categories are only numbers. This also has the advantage that the JsonBean
s could contain special annotations for deserialization like they are used by Jackson
.
The second layer will be the object graph that is used in the application and it replaces the child-categories with concrete objects. Let's simply call this type of classes Bean
. The Bean
s would be independent from the design of the JSON, in such a way, that they could be reused even if the data is loaded from a database instead of a JSON.
The deserialization process would then have two steps: First the raw deserialization into JsonBean
s. Then followed by a transformation service that would copy the JsonBean
s to the application Bean
s. This transformation would then resolve the ids of the child-categories to concrete Category
-objects.
The separation between the pure value model and the persistence model also gives some safety in case the JSON's structure is changed in the future. As long as the change is not too severe, this would have little effect on your application, since you would only need to change the JsonBean
s and the transformation - not everything else that uses the data after the deserialization.
答案2
得分: 0
这是我的解决方案
概要
- 修改了文章中的类模型 - 使用[组合设计模式][1]添加了树结构
- 使用Jackson库将JSON反序列化为文章中的类模型。
- 构建一个
Map<Integer, Category>
,允许通过其ID直接访问类别 - 遍历类别,对于每个类别,遍历其子类别,对于每个子类别ID,从映射中获取子类别并分配为子实例
- 最后,使用[深度优先搜索][2]遍历树并使用[访问者模式][3]打印类别
数据结构
我拿了你的模型并做了一些修改:
- 将列表更改为接口定义。这是因为变量构建是由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;
// 树结构属性
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;
// 树结构属性
public List<Category> tree;
// 使用打印机访问者启动每个根的遍历
public void printTree() {
Visitor<Category> printer = new Printer();
tree.forEach(root -> root.visit(printer, 0));
}
}
解决方案实施
访问者模式
// 访问者接口
public interface Visitor<T>
{
/**
* @param level 0表示根,1表示根的子类别,依此类推
*/
public void accpet(T node, int level);
}
// 打印机访问者:适当缩进地将每个访问的类别打印到控制台
public class Printer implements Visitor<Category>
{
@Override
public void accpet(Category node, int level) {
System.out.println(indentByLevel(level) + node.id + " " + node.name);
// 如果有产品的类别 - 打印它们和变体
if (node.products != null && !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 && !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();
}
}
// 读取和解析源到Data对象
// 使用Java 11的HttpURLConnection和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) {
// 填充类别数组。数组索引是类别ID
// 这将允许通过其ID随机访问类别
Map<Integer, Category> categoryById = data.categories.stream()
.collect(Collectors.toMap(cat -> cat.id, Function.identity(), (cat1, cat2) -> cat1));
// 根据子ID列表为每个类别对象填充类别对象的列表
// 在此过程中,将每个子类别标记为非根
data.categories.stream()
.filter(cat -> cat.childCategoryIds != null && !cat.childCategoryIds.isEmpty())
.forEach(catWithChildren ->
catWithChildren.childCategoryIds.forEach(id -> {
Category child = categoryById.get(id);
if (child != null) {
catWithChildren.childCategories.add(child);
child.isRoot = false;
}
})
);
// 从根类别构建树
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
<details>
<summary>英文:</summary>
Here is my solution
Outline
----
1. modify the class model from the post - add tree structure using [Composite design pattern][1]
1. deserialize the json into the class model from the post using Jackson library.
1. build a `Map<Integer, Category>` that allows direct access to category by its id
1. iterate over the categories, for each one, iterate over its children, for each child id, get the child category from the map and assign as child instance
1. finally, iterate over the tree using [depth first search][2] and print the categories using [visitor pattern][3]
Data Structure
----
I took your model and modified a little bit:
1. Changed lists to be defined as the interface. this is since the variable construction is performed by the json deserializer that may decide to use a different implementation of `List`
1. Added collections for the implementation of Composite pattern: each `Category` has list of child categories as `Category` instances, `Data` was added list of tree roots.
1. Changed variable names to better reflect purpose and usage.
Here is how the modified `Data` and `Category` classes look like:
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));
}
}
Solution Implementation
----
**Visitor Pattern**
// 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 && !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 && !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 "";
}
}
**Main class**
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 && !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));
}
}
Printout
----
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 & 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
[1]: https://en.wikipedia.org/wiki/Composite_pattern
[2]: https://en.wikipedia.org/wiki/Depth-first_search
[3]: https://en.wikipedia.org/wiki/Visitor_pattern
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论