这是我的解决方案
大纲
- 使用Composite design pattern修改后添加树结构的类模型
- 使用 Jackson 库将帖子中的 json 反序列化为类模型。
- 构建一个
Map<Integer, Category>,允许通过其 id 直接访问类别
- 遍历类别,对于每个类别,遍历其子类别,对于每个子 ID,从地图中获取子类别并指定为子实例
- 最后,使用depth first search 遍历树并使用visitor pattern 打印类别
数据结构
我拿了你的模型,稍微修改了一下:
- 已更改列表定义为接口。这是因为变量构造是由可能决定使用
List 的不同实现的 json 反序列化器执行的
- 为实现复合模式添加了集合:每个
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 && !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();
}
}
// 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));
}
}
打印输出
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