找到顶级分类父级,在示例JSON中使用Java添加相应父级下方的子级。

huangapple go评论182阅读模式
英文:

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 JsonBeans - 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 JsonBeans 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 Beans 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 JsonBeans. Then followed by a transformation service that would copy the JsonBeans to the application Beans. 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 JsonBeans and the transformation - not everything else that uses the data after the deserialization.

答案2

得分: 0

这是我的解决方案

概要

  1. 修改了文章中的类模型 - 使用[组合设计模式][1]添加了树结构
  2. 使用Jackson库将JSON反序列化为文章中的类模型。
  3. 构建一个Map<Integer, Category>,允许通过其ID直接访问类别
  4. 遍历类别,对于每个类别,遍历其子类别,对于每个子类别ID,从映射中获取子类别并分配为子实例
  5. 最后,使用[深度优先搜索][2]遍历树并使用[访问者模式][3]打印类别

数据结构

我拿了你的模型并做了一些修改:

  1. 将列表更改为接口定义。这是因为变量构建是由JSON反序列化器执行的,可能决定使用List的不同实现。
  2. 添加了用于实现组合模式的集合:每个Category都有子类别的列表作为Category实例,Data添加了树根的列表。
  3. 更改了变量名称以更好地反映用途和使用。

以下是修改后的DataCategory类的外观:

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&lt;Integer, Category&gt;` 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&lt;Product&gt; products;
		@JsonProperty(&quot;child_categories&quot;)
		public List&lt;Integer&gt; childCategoryIds;

		// tree-structure properties
		public boolean isRoot = true;
		public List&lt;Category&gt; childCategories = new ArrayList&lt;&gt;();

		public void visit(Visitor&lt;Category&gt; visitor, int level) {
			visitor.accpet(this, level);
			childCategories.forEach(cat -&gt; cat.visit(visitor, level + 1));
		}
	}

	public class Data
	{
		public List&lt;Category&gt; categories;
		public List&lt;Rank&gt; rankings;
	
		// tree-structure properties
		public List&lt;Category&gt; tree;
	
		// initiate traversal of each root withj printer visitor
		public void printTree() {
			Visitor&lt;Category&gt; printer = new Printer();
			tree.forEach(root -&gt; root.visit(printer, 0));
		}
	}

Solution Implementation
----

**Visitor Pattern**

	// visitor interface
	public interface Visitor&lt;T&gt;
	{
		/**
		 * @param level 0 is root, 1 root&#39;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&lt;Category&gt;
	{
		@Override
		public void accpet(Category node, int level) {
			System.out.println(indentByLevel(level) + node.id + &quot; &quot; + 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 -&gt; {
				System.out.println(indentByLevel(level+1) + product.id + &quot; &quot; + product.name);
				if (product.variants != null  &amp;&amp;  !product.variants.isEmpty()) {
					product.variants.forEach(variant -&gt; {
						System.out.println(indentByLevel(level+2) + variant.id + &quot; &quot; + variant.color);
					});
				}
			});
		}

		private static final String levelIndent = &quot;    &quot;;
		private String indentByLevel(int level) {
			if (level &gt; 0) {
				return String.join(&quot;&quot;, Collections.nCopies(level, levelIndent));
			}
			return &quot;&quot;;
		}
	}
	
**Main class**

	public class JsonToTree
	{
		public static void main(String[] args) {
			try {
				Data data = readJson(&quot;https://stark-spire-93433.herokuapp.com/json&quot;);
				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(&quot;GET&quot;);
			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&lt;Integer, Category&gt; categoryById = data.categories.stream()
				.collect(Collectors.toMap(cat -&gt; cat.id, Function.identity(), (cat1, cat2) -&gt; 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 -&gt; cat.childCategoryIds != null  &amp;&amp;  !cat.childCategoryIds.isEmpty())
				.forEach(catWithChildren -&gt;
					catWithChildren.childCategoryIds.forEach(id -&gt; {
						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&lt;&gt;();
			data.categories.stream()
				.filter(cat -&gt; cat.isRoot)
				.forEach(root -&gt; 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 &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




  [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>



huangapple
  • 本文由 发表于 2020年9月10日 15:05:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/63824439.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定