【问题标题】:Find top category parent and add child below respective parent in sample JSON using java使用 java 在示例 JSON 中查找顶级类别父级并在相应父级下方添加子级
【发布时间】:2021-11-06 06:44:12
【问题描述】:

我找到了这个示例电子商务 JSON。我想按层次排列这个 JSON。

您可以在以下 URL 上找到此 JSON 文件。

https://stark-spire-93433.herokuapp.com/json

我想按以下方式排列这个 JSON。

Mens Wear
    |Bottom Wear
        |Jeans
        |Tracks & Trousers
    |Foot Wear
        |Casuals
        |Formals
    |Upper Wear
        |Shirts
        |T-Shirts
Electronics
    |Mobiles
        |Apple
        |Samsung
    |Laptops
        |Dell
        |Toshiba
        

所有变体都将位于产品的最后一个层次结构中。例如戴尔、苹果、衬衫等。

我已经为这个 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;
}

现在我不明白我应该如何开始。

【问题讨论】:

  • 我建议不要使用任何对象映射。我会准备一些类来呈现您想要的树结构,就像您需要以编程方式构建树一样。例如,您的 Category 不应包含 ArrayList child_categories,而是 ArrayList children... 之后,使用面向流的解析 baeldung.com/jackson-streaming-api 并根据需要构建遍历 JSON 令牌的树。跨度>

标签: java json


【解决方案1】:

这是我的解决方案

大纲

  1. 使用Composite design pattern修改后添加树结构的类模型
  2. 使用 Jackson 库将帖子中的 json 反序列化为类模型。
  3. 构建一个Map&lt;Integer, Category&gt;,允许通过其 id 直接访问类别
  4. 遍历类别,对于每个类别,遍历其子类别,对于每个子 ID,从地图中获取子类别并指定为子实例
  5. 最后,使用depth first search 遍历树并使用visitor pattern 打印类别

数据结构

我拿了你的模型,稍微修改了一下:

  1. 已更改列表定义为接口。这是因为变量构造是由可能决定使用 List 的不同实现的 json 反序列化器执行的
  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;

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

【讨论】:

    【解决方案2】:

    嗯,问题在于使用 id 间接引用子类别。目标是将间接引用转换为直接引用,例如 data.getCategory().getChildCategory().getName() 是可能的。

    我会通过引入两个抽象层来解决这个问题:

    第一个仅用于反序列化。该层的类——我们称它们为JsonBeans——与json的结构非常相似,因此子类别只是数字。这还有一个优点,JsonBeans 可以包含用于反序列化的特殊注释,就像 Jackson 使用的那样。

    第二层将是应用程序中使用的对象图,它将子类别替换为具体对象。让我们简单地称这种类型的类为BeanBeans 将独立于 JSON 的设计,即使数据是从数据库而不是 JSON 加载的,它们也可以被重用。

    反序列化过程将分为两个步骤:首先将原始反序列化为JsonBeans。然后是一个转换服务,它将JsonBeans 复制到应用程序Beans。然后,此转换会将子类别的 id 解析为具体的Category-objects。

    纯值模型和持久性模型之间的分离也提供了一些安全性,以防 JSON 的结构在未来发生变化。只要更改不太严重,这对您的应用程序影响不大,因为您只需要更改 JsonBeans 和转换 - 而不是在反序列化后使用数据的所有其他内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多