【问题标题】:Json Array Flattening with Jackson / Parsing PerformanceJson Array Flattening with Jackson / 解析性能
【发布时间】:2021-05-24 08:41:57
【问题描述】:

我有一个像下面这样的 JSON: 我的目标 POJO 是

[{
        "id": "1",
        "teams": [{
                "name": "barca"
            },
            {
                "name": "real"
            }
        ]
    },
    {
        "id": "2"
    },
    {
        "id": "3",
        "teams": [{
                "name": "atletico"
            },
            {
                "name": "cz"
            }
        ]
    }
]

我的目标 POJO 是

class Team
int id;
String name;

意思是,我想为每个“团队”创建一个新对象。喜欢;

new Team(1,barca)
new Team(1,real)
new Team(2,null)
new Team(3,atletico)
...

我相信我使用如下自定义反序列化器做到了这一点:

            JsonNode rootArray = jsonParser.readValueAsTree();
            for (JsonNode root : rootArray) {
                String id = root.get("id").toString();
                JsonNode teamsNodeArray = root.get("teams");
                if (teamsNodeArray != null) {
                    for (JsonNode teamNode: teamsNodeArray ) {
                        String nameString = teamNode.get("name").toString();
                        teamList.add(new Team(id, nameString));
                    }
                } else {
                    teamList.add(new Team(id, null));
                }
            }

考虑到我获得了 750k 条记录...拥有 2 个 fors 是我相信使代码速度比应有的速度慢。大约需要 7 分钟。

我的问题是,如果有更好的方法可以请您赐教吗?

PS:我已经为此检查了许多 stackoverflow 线程,但目前找不到任何适合的内容。

提前谢谢你。

【问题讨论】:

    标签: java json jackson


    【解决方案1】:

    不要自己解析数据,尽可能使用自动反序列化。

    使用 jackson 可以很简单:

    MyData myData = new ObjectMapper().readValue(rawData, MyData.class);
    

    对于您的具体示例,我们生成一个非常大的实例(10M 行):

    $ head big.json 
    [{"id": 1593, "group": "6141", "teams": [{"id": 10502, "name": "10680"}, {"id": 16435, "name": "18351"}]}
    ,{"id": 28478, "group": "3142", "teams": [{"id": 30951, "name": "3839"}, {"id": 25310, "name": "19839"}]}
    ,{"id": 29810, "group": "8889", "teams": [{"id": 5586, "name": "8825"}, {"id": 27202, "name": "7335"}]}
    ...
    $ wc -l big.json 
    10000000 big.json
    

    然后,定义与您的数据模型匹配的类(例如):

    public static class Team {
        public int id;
        public String name;
    }
    
    public static class Group {
        public int id;
        public String group;
        public List<Team> teams;
    }
    

    现在您可以通过以下方式直接读取数据:

    List<Group> xs = new ObjectMapper()
                       .readValue(
                           new File(".../big.json"),
                           new TypeReference<List<Group>>() {});
    

    完整的代码可以是:

    public static void main(String... args) throws IOException {
    
        long t0 = System.currentTimeMillis();
    
        List<Group> xs = new ObjectMapper().readValue(new File("/home/josejuan/tmp/1/big.json"), new TypeReference<List<Group>>() {});
    
        long t1 = System.currentTimeMillis();
    
        // test: add all group id
        long groupIds = xs.stream().mapToLong(x -> x.id).sum();
    
        long t2 = System.currentTimeMillis();
    
        System.out.printf("Group id sum := %d, Read time := %d mS, Sum time = %d mS%n", groupIds, t1 - t0, t2 - t1);
    }
    

    有输出:

    Group id sum := 163827035542, Read time := 10710 mS, Sum time = 74 mS
    

    只需 11 秒 即可解析 10M 行

    为了检查数据和比较性能,我们可以直接从磁盘读取:

    $ perl -n -e 'print "$1\n" if /"id": ([0-9]+), "group/' big.json | time awk '{s+=$1}END{print s}'
    163827035542
    4.96user
    

    使用 5 秒Java 代码只有一半的速度)。

    处理数据的非性能问题可以通过多种方式解决,具体取决于您希望如何使用信息。例如,可以对所有团队进行分组:

    List<Team> teams = xs.stream()
                         .flatMap(x -> x.teams.stream())
                         .collect(toList());
    
    Map<Integer, Team> uniqTeams = xs.stream()
                                     .flatMap(x -> x.teams.stream())
                                     .collect(toMap(
                                          x -> x.id,
                                          x -> x,
                                          (a, b) -> a));
    

    【讨论】:

    • 嗨...想过这个,但这个解决方案并没有给我想要的“扁平化”。我想如果我想要这种方式,我可以只使用注释......我的意思是从内部数组创建新实例。
    • 嗨@Bleach 我不太了解您的数据结构(例如name 字段未出现在json 中),也不了解您需要如何聚合它。无论如何,一旦你有了数据,这应该不难,因为 7 分钟的性能损失是由你如何解析 json(而不是循环)造成的。
    • 嗨,很抱歉我在源 json 中打错了字。现在我编辑了它,name 就在那里。所以你的意思是在这种情况下性能损失是不可避免的?
    • 性能问题("Only 11 seconds to parse 10M rows.")和转换问题都得到了解决。花点时间了解答案并尝试将其应用于您的案例:)
    • 非常详细的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-02
    • 2012-07-05
    • 2018-06-30
    相关资源
    最近更新 更多