json多层次数据统计结构生成

  • 项目中有统计的需求,一开始想使用pojo来封装统计的数据但是发现每个统计需求是特有的,也就是说写pojo是可以解决问题的,但是只是用一次统计就不用了,而且后期统计需求增加,pojo会大量增加,所以就思考是否有更好的解决方案

思路:前后端交互用的是json数据,json的结构抽象出来就是一棵树或多棵树,所以生成统计数据实际就是生成一棵树,生成一棵树实际就是对节点的操作(CRUD),所以要统计数据就是要自定义一棵可以过滤的树,当一个统计数据(如:性别)流经这棵树时,首先会根据给定的筛选节点进行流动,遇到分支会进行判断,流向特定的分支,如发现判断后没有给定节点,将会生成新的节点,最后将统计结果放在树的分支的最后一个节点上,从而实现了相对自由的统计结构生成方式

环境:springboot

依赖:net.sf.json.JSONObject(用其它也是可以的,思路一样方法不一样而已)

import net.sf.json.JSONObject;
import java.util.*;

/**
 * @author ZC
 * @date 2019/12/23
 */
public class Test {
    public static void main(String[] args) {

        //模拟数据
        List<User> userList = new ArrayList<>();
        userList.add(new User(1, "1997", "IT部"));
        userList.add(new User(0, "1992", "销售部"));
        userList.add(new User(0, "1999", "IT部"));
        userList.add(new User(1, "1995", "IT部"));
        userList.add(new User(0, "1995", "销售部"));
        userList.add(new User(1, "1997", "IT部"));
        userList.add(new User(0, "1995", "运维部"));
        userList.add(new User(1, "1997", "IT部"));
        userList.add(new User(0, "1997", "运维部"));
        userList.add(new User(1, "1995", "IT部"));
        userList.add(new User(0, "1997", "运维部"));
        userList.add(new User(1, "1992", "运维部"));
        userList.add(new User(1, "1992", "IT部"));

        //统计数据的根节点
        JSONObject statistics = new JSONObject();
        //过滤的一些条件和统计数据
        HashMap<String, Object> condittion = new HashMap<>();

        //设置根条件(属于哪个公司)
        condittion.put("com", "哈皮公司");
        //统计
        for (User user : userList) {
            //设置子条件和统计数据
            condittion.put("gender", user.getSex());
            condittion.put("dep", user.getDep());
            condittion.put("year", user.getBirthday());
            //开始统计过滤
            jsonDepInc(statistics, condittion);
        }
        System.out.println(statistics);
    }

    /**
     * @param jsonObject 需要按节点生成统计数据的JSONObject对象
     * @param condition  条件
     * @return void
     * @throws
     * @description 部门统计
     * @author ZC
     * @date 2019/12/24
     */
    private static void jsonDepInc(JSONObject jsonObject, Map<String, Object> condition) {

        String dep = (String) condition.get("dep");
        int gender = (int) condition.get("gender");
        String year = (String) condition.get("year");
        String com = (String) condition.get("com");

        // 以下字符串可以统一定义出去,使用condition传进来,
        // 这样便于节点名称的管理,其实就是统计树上的所有节点名称
        jsonInc(jsonObject, com, "count");
        if (gender == 1) {
            jsonInc(jsonObject, com, "gender", "male");
        } else {
            jsonInc(jsonObject, com, "gender", "female");
        }
        jsonInc(jsonObject, com, dep, "count");
        if (gender == 1) {
            jsonInc(jsonObject, com, dep, "gender", "male");
        } else {
            jsonInc(jsonObject, com, dep, "gender", "female");
        }
        jsonInc(jsonObject, com, dep, "year", year);
    }

    /**
     * @param jsonObject 需要按节点生成统计数据的JSONObject对象
     * @param keys       最终需要加一的节点的访问路径
     * @return void
     * @throws
     * @description 按json节点顺序,对最后一个节点进行自增,建议做为工具方法
     * @author ZC
     * @date 2019/12/24
     */
    private static void jsonInc(JSONObject jsonObject, String... keys) {
        LinkedList<String> jsonKeys = new LinkedList<>();
        //复制一份String到List中方便节点操作
        for (String key : keys) {
            jsonKeys.add(key);
        }
        jsonIncModifyList(jsonObject, jsonKeys);
    }

    /**
     * @param jsonObject 需要按节点生成统计数据的JSONObject对象
     * @param keys       最终需要加一的节点的访问路径
     * @return void
     * @throws
     * @description 按json节点顺序,对最后一个节点进行自增,List会改变
     * @author ZC
     * @date 2019/12/24
     */
    private static void jsonIncModifyList(JSONObject jsonObject, LinkedList<String> keys) {

        //获取第一个节点
        String key = keys.get(0);
        if (keys.size() != 1) {//不是最后一个节点时
            if (!jsonObject.containsKey(key)) {//当该节点不存在
                JSONObject innerJsonObject = new JSONObject();
                keys.remove(0);
                //进入下一层节点
                jsonIncModifyList(innerJsonObject, keys);
                //递归回来将新增的有值节点存入
                jsonObject.element(key, innerJsonObject);
            } else {//当该节点存在
                keys.remove(0);
                //继续访问下一层节点
                jsonIncModifyList(jsonObject.getJSONObject(key), keys);
            }
        } else {//是最后一个节点时
            if (jsonObject.containsKey(key)) {
                int incNum = jsonObject.getInt(key);
                incNum++;
                jsonObject.element(key, incNum);
            } else {
                jsonObject.element(key, 1);
            }
        }
    }
}

class User {
    private Integer gender;
    private String birthday;
    private String dep;

    User() {
    }

    public User(Integer gender, String birthday, String dep) {
        this.gender = gender;
        this.birthday = birthday;
        this.dep = dep;
    }

    public Integer getSex() {
        return gender;
    }

    public void setSex(Integer gender) {
        this.gender = gender;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getDep() {
        return dep;
    }

    public void setDep(String dep) {
        this.dep = dep;
    }
}

生成结果:

{
    "哈皮公司":{
        "count":13,
        "gender":{
            "male":7,
            "female":6
        },
        "IT部":{
            "count":7,
            "gender":{
                "male":6,
                "female":1
            },
            "year":{
                "1992":1,
                "1995":2,
                "1997":3,
                "1999":1
            }
        },
        "销售部":{
            "count":2,
            "gender":{
                "female":2
            },
            "year":{
                "1992":1,
                "1995":1
            }
        },
        "运维部":{
            "count":4,
            "gender":{
                "female":3,
                "male":1
            },
            "year":{
                "1992":1,
                "1995":1,
                "1997":2
            }
        }
    }
}

船新版本

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;

/**
 * @author ZC
 * @date 2019/12/23
 */
public class Test {
    public static void main(String[] args) {

        //根节点
        JSONObject jsonObject = new JSONObject();

        //Number的子类都可以参与运算
        jsonInc(jsonObject, new Short("8"), "哈皮公司", "描述");
        jsonInc(jsonObject, new Short("2"), "哈皮公司", "描述");
        jsonInc(jsonObject, new Double("0.1"), "哈皮公司", "描述");
        jsonInc(jsonObject, new Byte("1"), "哈皮公司", "描述");
        jsonInc(jsonObject, new Float("1.2"), "哈皮公司", "描述");
        System.out.println(jsonObject);
        //实现精确计算
        jsonInc(jsonObject, -0.2, "哈皮公司", "描述");
        jsonInc(jsonObject, 0.1889, "哈皮公司", "描述");
        System.out.println(jsonObject);
        //可添加JSONObject可转化的对象
        jsonInc(jsonObject, "介是一个公司", "哈皮公司", "描述");
        System.out.println(jsonObject);
        //实现精确计算
        jsonInc(jsonObject, 0.1889, "哈皮公司", "描述");
        System.out.println(jsonObject);
        jsonInc(jsonObject, -1.1889, "哈皮公司", "描述");
        System.out.println(jsonObject);
        //可添加JSONObject可转化的对象
        jsonInc(jsonObject, new JSONObject().element("内部JSONObject描述", "不可描述"), "哈皮公司", "描述");
        System.out.println(jsonObject);
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put("内部HashMap描述", 5);
        jsonInc(jsonObject, hashMap, "哈皮公司", "描述");
        System.out.println(jsonObject);
        jsonInc(jsonObject, new JSONArray(), "JSONArray", "描述");
        System.out.println(jsonObject);
    }

    /**
     * @param jsonObject 需要按节点生成统计数据的JSONObject对象
     * @param keys       最终需要追加对象的节点的访问路径
     * @param addObj     数字做运算,对象做追加,如果最后一个节点原来已经有对象,将会覆盖
     * @description 按json节点顺序,对最后一个节点添加元素或运算
     * @author ZC
     * @date 2019/12/24
     */
    private static void jsonInc(JSONObject jsonObject, Object addObj, String... keys) {
        //复制一份String到List中方便节点操作
        LinkedList<String> jsonKeys = new LinkedList<>(Arrays.asList(keys));
        jsonIncModifyList(jsonObject, addObj, jsonKeys);
    }

    private static void jsonIncModifyList(JSONObject jsonObject, Object addObj, LinkedList<String> keys) {

        //获取第一个节点
        String key = keys.get(0);
        if (keys.size() != 1) {//不是最后一个节点时
            if (!jsonObject.containsKey(key)) {//当该节点不存在
                JSONObject innerJsonObject = new JSONObject();
                keys.remove(0);
                //进入下一层节点
                jsonIncModifyList(innerJsonObject, addObj, keys);
                //递归回来将新增的有值节点存入
                jsonObject.element(key, innerJsonObject);
            } else {//当该节点存在
                keys.remove(0);
                //key对应的不是JSONObject,新建一个覆盖原对象
                //此处成立说明对应的访问节点已经存储了非JSONObject对象,如:字符串,集合等
                if (!(jsonObject.get(key) instanceof JSONObject)) {
                    jsonObject.element(key, new JSONObject());
                }
                //继续访问下一层节点
                jsonIncModifyList(jsonObject.getJSONObject(key), addObj, keys);
            }
        } else {//是最后一个节点时

            if (addObj instanceof Number) {//对数字,实现精确计算
                //入参的数字
                BigDecimal num = new BigDecimal(String.valueOf(addObj));
                if (jsonObject.containsKey(key)) {
                    //将最后一个节点value不是数字类型的初始化为0
                    if (!(jsonObject.get(key) instanceof Number)) {
                        jsonObject.element(key, 0);
                    }
                    //末节点的数字
                    BigDecimal sum = new BigDecimal(String.valueOf(jsonObject.get(key)));
                    sum = sum.add(num);
                    jsonObject.element(key, sum);
                } else {
                    //初始化数字
                    jsonObject.element(key, num);
                }
            } else {//对其它对象,如:字符串,新的jsonObject,集合等
                jsonObject.element(key, addObj);
            }
        }
    }
}

生成结果:

{"哈皮公司":{"描述":12.3}}
{"哈皮公司":{"描述":12.2889}}
{"哈皮公司":{"描述":"介是一个公司"}}
{"哈皮公司":{"描述":0.1889}}
{"哈皮公司":{"描述":-1}}
{"哈皮公司":{"描述":{"内部JSONObject描述":"不可描述"}}}
{"哈皮公司":{"描述":{"内部HashMap描述":5}}}
{"哈皮公司":{"描述":{"内部HashMap描述":5}},"JSONArray":{"描述":[]}}

结语:如果大家有新的想法和处理方式请在下方评论,不足之处也请告知,共勉!

发布了43 篇原创文章 · 获赞 12 · 访问量 9856

猜你喜欢

转载自blog.csdn.net/OrangeHap/article/details/103694120