【02上篇】JSON比较——JAVA之利用JSON数据unescapeJava / WEB之利用reflect-metadata(实战操作)

公司需求如下:用户当前操作和上一次保存操作进行对比,然后显示到前端web弹窗,首先上一次操作毕竟是日志,包含了各种各样的操作,同事就用类外加整个对象来标识到底是对什么进行了操作,就才会有如下的className这个东西,同时要对当前的操作,也就是update的整个对象和之前存储日志的className这个东西进行对比,json的比较就是第一步!

JAVADEMO

package com.gisquest.tdcb.tdcbitem.utils;


import com.alibaba.fastjson.JSONObject;
import com.gisquest.realestate.utils.StringUtils;
import org.apache.commons.lang3.StringEscapeUtils;

import java.util.Iterator;

/**
 * Created by Administrator on 2019/7/20.
 */
public class Demo {

    public static void main(String[] args) {
        String className = "yscdkData";

        StringBuffer sb = new StringBuffer();

        // String st1 = "{\"dmj\":1111.0}";
        // String st1 = null;
        // String st2 = "{\"yscdkData\":{\"dmj\":33.0,\"dswQksm\":\"22\"}}";

        /* before */
        String st1 = "{\"dmj\":1111.0,\"dswQksm\":\"22\",\"xmCj\":\"3\",\"cqMj\":0,\"sfyCbz\":\"1\",\"yxgmMj\":0,\"lmj\":1111.0,\"pcXzmj\":3,\"yxgmZc\":0,\"tdqqKfcb\":-1,\"qtfsqdMj\":0,\"pgFs\":\"2\",\"zjCb\":0,\"zyzyZc\":0,\"qtZc\":0,\"tdjb\":\"2\",\"sjCb\":-1,\"sgMj\":0,\"tdsgZc\":0,\"dwMc\":\"杭州市余杭区土地储备发展中心\",\"zdBh\":\"201901019\",\"nrCbLy\":\"4\",\"haveJzd\":0,\"sbSj\":1562169600000,\"sflyDxtd\":\"0\",\"sflyXztd\":\"1\",\"dswQk\":\"1\",\"xmZt\":\"13\",\"ghFs\":\"3\",\"zdZl\":\"临平新城核心区A-3-06拟招拍挂地块(东至星河南路、南至文正街、西至河道、北至东仁街)\",\"clMj\":0,\"lydxtdMj\":0,\"zszyMj\":1,\"xzqDm\":\"330110\",\"dkMc\":\"e\",\"pgj\":333,\"jzdjnh\":\"3\",\"qtCb\":0,\"qqKfqk\":\"222\",\"createDate\":1562169600000,\"zdMj\":1,\"dzBaBh\":\"3301102015S00635\",\"cqbcazZc\":0,\"nrCbSj\":1562169600000,\"tCbGhyt\":[{\"modifyDate\":1562169600000,\"ghytGuid\":\"c1be0530-a06e-fdf4-78a9-9839245c4e1a\",\"ghytMj\":1,\"tdYt\":\"6\",\"cbGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"createDate\":1562169600000}],\"cbjgGuid\":\"6b70a1e4-cb83-1e9f-9aca-c8a53f8a1b30\",\"sczGuid\":\"9d7dffc2-9c74-4671-82cd-d7a902112d65\",\"shMj\":0,\"tdshZc\":0,\"tdmqzt\":\"333\",\"rkDkbh\":\"1212\",\"tCbCbglpws\":[{\"zpcmj\":111,\"cbglpwGuid\":\"42f44bca-ab7f-66b8-84ec-f1b3dc882724\",\"xzMj\":3,\"cbdkGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"pzWh\":\"333\"}],\"tCbzs\":[{\"cbzBh\":\"3\",\"modifyDate\":1563465600000,\"zzMj\":3,\"qlrMc\":\"3\",\"cbzGuid\":\"cc616b0f-606f-887c-dcaa-6ee5527e8404\",\"cbGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"createDate\":1563465600000}],\"createUser\":\"yhcbjg\",\"tdQdCb\":0,\"isNdzcpg\":\"0\",\"cbGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"lyxztdMj\":0}";
        /* after */
        String st2 = "{\"yscdkData\":{\"dmj\":1111.0,\"dswQksm\":\"22\",\"xmCj\":\"3\",\"cqMj\":0,\"sfyCbz\":\"1\",\"yxgmMj\":0,\"lmj\":1111.0,\"pcXzmj\":3,\"yxgmZc\":0,\"tdqqKfcb\":-1,\"qtfsqdMj\":0,\"pgFs\":\"2\",\"zjCb\":0,\"zyzyZc\":0,\"qtZc\":0,\"tdjb\":\"2\",\"sjCb\":-1,\"sgMj\":0,\"tdsgZc\":0,\"dwMc\":\"杭州市余杭区土地储备发展中心\",\"zdBh\":\"201901019\",\"haveJzd\":0,\"sbSj\":1562169600000,\"sflyDxtd\":\"0\",\"sflyXztd\":\"1\",\"dswQk\":\"1\",\"xmZt\":\"13\",\"ghFs\":\"3\",\"zdZl\":\"临平新城核心区A-3-06拟招拍挂地块(东至星河南路、南至文正街、西至河道、北至东仁街)\",\"clMj\":0,\"lydxtdMj\":0,\"zszyMj\":1,\"xzqDm\":\"330110\",\"dkMc\":\"4445ABCD\",\"pgj\":333,\"jzdjnh\":\"3\",\"qtCb\":0,\"qqKfqk\":\"222\",\"createDate\":1562169600000,\"zdMj\":1,\"cqbcazZc\":0,\"nrCbSj\":1562169600000,\"tCbGhyt\":[{\"modifyDate\":1562169600000,\"ghytGuid\":\"c1be0530-a06e-fdf4-78a9-9839245c4e1a\",\"ghytMj\":1,\"tdYt\":\"6\",\"cbGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"createDate\":1562169600000}],\"cbjgGuid\":\"6b70a1e4-cb83-1e9f-9aca-c8a53f8a1b30\",\"sczGuid\":\"9d7dffc2-9c74-4671-82cd-d7a902112d65\",\"shMj\":0,\"tdshZc\":0,\"tdmqzt\":\"333\",\"rkDkbh\":\"1212\",\"tCbCbglpws\":[{\"zpcmj\":111,\"cbglpwGuid\":\"42f44bca-ab7f-66b8-84ec-f1b3dc882724\",\"xzMj\":3,\"cbdkGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"pzWh\":\"333\"}],\"tCbzs\":[{\"cbzBh\":\"3\",\"modifyDate\":1563465600000,\"zzMj\":3,\"qlrMc\":\"3\",\"cbzGuid\":\"cc616b0f-606f-887c-dcaa-6ee5527e8404\",\"cbGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"createDate\":1563465600000}],\"createUser\":\"yhcbjg\",\"isNdzcpg\":\"0\",\"cbGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"lyxztdMj\":0}}";

        getCompareOpt(className, sb, st1, st2);
    }

    /**
     * 旧数据和当前保存的数据进行比较,,返回比较的字符串,不是json
     *
     * @param className
     * @param sb
     * @param str1
     * @param str2
     * @return
     * @date Xuhy, 2019-07-20 14:57
     */
    public static String getCompareOpt(String className, StringBuffer sb, String str1, String str2) {
        if (!StringUtils.isEmpty(str1)) {
            // 取出st1属性数组
            Iterator<String> st1keyArrs = JSONObject.parseObject(str1).keySet().iterator();

            // 取出st2属性数组--当前是根据我公司的数据结构,可以按照情况自行修改
            JSONObject jsonObject2 = JSONObject.parseObject(str2).getJSONObject(className);
            // System.out.println(str1);
            // System.out.println(jsonObject2);
            // 取出st2属性数组
            Iterator<String> st2keyArrs = jsonObject2.keySet().iterator();

            // 将st1和st2转换成json进行json比较
            JSONObject json1 = JSONObject.parseObject(str1);
            JSONObject json2 = JSONObject.parseObject(JSONObject.parseObject(str2).get(className).toString());

            // 前json和后json比较--返回是修改和删除
            getCompareBA(st1keyArrs, json1, json2, sb);
            // 后json和前json比较--返回是新增
            getCompareAB(st2keyArrs, json1, json2, sb);


            String compareOpt = "{\"className\":\"" + className + "\",\"changeContent\":[" + sb.substring(0, sb.length() - 1) + "]}";
            System.out.println("--------------------compareOpt--------------------");
            System.out.println(compareOpt);
            System.out.println("--------------------compareOpt--------------------");
            return compareOpt;
        } else {
            String compareOpt = "{\"className\":\"" + className + "\",\"changeContent\":[" + JSONObject.parseObject(str2).toString() + "]}";
            System.out.println("--------------------compareOptOrg--------------------");
            System.out.println(compareOpt);
            System.out.println("--------------------compareOptOrg--------------------");
            return compareOpt;
        }
    }


    /**
     * @Description: 前JSON和后JSON比较
     * @Param: beforeKeySet
     * @Param: afterJson
     * @Return: 之前和之后的删除和修改操作
     * @CreateDate: Xuhaiyan
     */
    private static void getCompareBA(Iterator<String> beforeKeySet, JSONObject beforeJson, JSONObject afterJson, StringBuffer sb) {
        while (beforeKeySet.hasNext()) {
            // 上比下
            String key = beforeKeySet.next();

            if (afterJson.get(key) == null) {
                sb.append("{\"" + key + "\": \"" + beforeJson.get(key) + "删除变成空\"},");
                // System.out.println("有变化--删除操作 key:" + key + ",value:" + afterJson.get(key));
            } else if (beforeJson.get(key) != null && afterJson.get(key) != null && !afterJson.get(key).equals(beforeJson.get(key))) {
                // 字符串转义至关重要,对于多层次的必须如下处理
                System.out.println();
                sb.append("{\"" + key + "\": \"" + StringEscapeUtils.escapeJava(afterJson.get(key).toString()) + "修改成" + StringEscapeUtils.escapeJava(beforeJson.get(key).toString()) + "\"},");
                // System.out.println("有变化--修改操作 key:" + key + ",value:" + afterJson.get(key));
            }
        }
    }

    /**
     * @Description: 后JSON和钱JSON比较
     * @Param: afterKeySet
     * @Param: beforeJson
     * @Return: 之后属性新增内容
     * @CreateDate: Xuhaiyan
     */
    private static void getCompareAB(Iterator<String> afterKeySet, JSONObject beforeJson, JSONObject afterJson, StringBuffer sb) {
        while (afterKeySet.hasNext()) {
            // 下比上
            String key = afterKeySet.next();

            if (beforeJson.get(key) == null) {
                //删除操作
                sb.append("{\"" + key + "\": \"新增" + beforeJson.get(afterJson.get(key).toString()) + "\"},");
            }
        }
    }
}

字符串转义demo

public class demo3 {
    public static void main(String[] args) {
        String str = "{\\\"name\\\":\\\"spy\\\"}";
        System.out.println("原始 str = " + str);
        String str1 = StringEscapeUtils.unescapeJava(str);
        String str2 = StringEscapeUtils.escapeJava(str);
        System.out.println("目标 str1 = " + str1);
        System.out.println("目标 str2 = " + str2);
    }
}

原始 str = {\"name\":\"spy\"}
目标 str1 = {"name":"spy"}
目标 str2 = {\\\"name\\\":\\\"spy\\\"}

转义必须添加上,才会组成jsong格式的字符串!!效果如下所示,多层次加转义,如果不是则不转义!!!

{"className":"yscdkData","changeContent":[{"zdBh": "1212修改成201901020"},{"zdZl": "ddd修改成\u4E34\u5E73\u65B0\u57CE\u6838\u5FC3\u533A\u8FCE\u5BBE\u8DEF\u4EE5\u897F\u3001\u671B\u6885\u8DEF\u4EE5\u5317\u5730\u5757(\u4E1C\u81F3\u8FCE\u5BBE\u8DEF\u3001\u5357\u81F3\u671B\u6885\u8DEF\u3001\u897F\u81F3\u65B0\u4E30\u8DEF\u3001\u5317\u81F3\u6587\u95F2\u8857)"},{"tCbGhyt": "[{\"modifyDate\":1562169600000,\"ghytGuid\":\"c1be0530-a06e-fdf4-78a9-9839245c4e1a\",\"ghytMj\":1,\"tdYt\":\"2\",\"cbGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"createDate\":1562169600000}]修改成[{\"modifyDate\":1562169600000,\"ghytGuid\":\"c1be0530-a06e-fdf4-78a9-9839245c4e1a\",\"ghytMj\":1,\"tdYt\":\"8\",\"cbGuid\":\"31bb176e-7c28-01be-d245-1bc0617a6916\",\"createDate\":1562169600000}]"},{"sczGuid": "686db12d-c008-42ad-bc37-3bfea21b7c69修改成027dc564-5557-4546-b642-1d3d431a37b2"}]}

WEBDEMO-ANGULAR7

第二步骤就是对整个返回的日志list,进行type筛选,如果是操作更新,则单独处理,毕竟普通人又不是程序员需要变换成中文展示

数据结构List

getServe() {
        // 假数据-徐海燕
        // this.operateLogDTO.ywGuid = 'f3fd665c-2427-7839-d647-474eb3cfadc0';
        // this.operateLogDTO.ywLx = 'TDCB_NSCDK';
        // 所有的未筛选数据
        let orgArrs = [];

        this.operateService.listOperateLog(this.operateLogDTO).subscribe(res => {
            if (res.status === 200 && res.data != null) {
                orgArrs = res.data.list;
                // 筛选数据排除[自定义比较的JSON数据]--xuhaiyan[upd]
                this.operateLogList = res.data.list.filter(compareOpt => compareOpt.operation != '操作变更');

                this._total = res.data.total;
                // 转换operateLogList, 把operateInfo转换为xx意见
                this.transferXXyj(this.operateLogList);
            }
            this._loading = false;
        }, error1 => {
        }, () => {
            const validator = new Validator();

            // 对于操作变更数据的操作--xuhaiyan[add]
            let compareOptArr = orgArrs.filter(compareOpt => compareOpt.operation === '操作变更');

            compareOptArr.forEach(c => {
                // 解析数据库的字符串[变成之前中文xx之后中文]
                let compareText = this.getCompareOpt(JSON.parse(c.operateInfo));
                // 覆盖原先的operateInfo 反转义unescape
                if (validator.isEmpty(compareText)) {
                    c.operateInfo = '请补充完整配置信息';
                } else {
                    c.operateInfo = eval('\'' + compareText + '\'');
                }

                // 放回原先的数组
                this.operateLogList.push(c);
            });


            console.log('实际上办理日志-----------------------------------------?????看我看我!');
            console.log(this.operateLogList);
            console.log('实际上办理日志-----------------------------------------?????看我看我!');
        });
    }
 //--------------------------------------------compareOptStr---------------------------------------------------------

    optionUpd = '修改成';

    /*optionKV = [
        // 拟收储
        {'tCbSczDk': ['dwMc|储备机构名称', 'zdBh|收储地块编号', 'xzqDm|座落行政区', 'zdMj|宗地面积', 'zdZl|座落', 'tdYt|规划用途', 'dzBaBh|电子监管号', 'pzSj|批准日期', 'ncdLy|拟取得方式']},

        //已收储【新增入库地块】
        {'yscdkData': ['zdBh|拟收储地块编号', 'rkDkbh|入库地块编号', 'dkMc|地块名称', 'zdZl|地块座落', 'zdMj|入库地块面积', 'nscDkMj|拟收储地块面积', 'pcXzmj|农转征新增面积合计', 'clMj|国有存量建设用地', 'zdZl|地块座落', 'zszyMj|实际征收和转用的农用地面积', 'cqMj|实际征收、拆迁的建设用地面积', 'shMj|实际依法收回的面积', 'sgMj|实际依法收购的面积', 'yxgmMj|实际行使优先购买权的面积', 'qtfsqdMj|实际通过其他方式取得的面积', 'sflyXztd|是否来源于闲置土地', 'lyxztdMj|来源于闲置土地面积', 'sflyDxtd|是否来源于低效/“三旧”改造土地', 'lydxtdMj|来源于低效/“三旧”改造土地面积', 'nrCbLy|取得方式', 'tdQdCb|实际发生成本', 'dswQk|地上物情况', 'dswQksm|地上物情况说明', 'qqKfqk|前期开发情况', 'nrCbSj|入库时间', 'ghFs|管护方式', 'lslyFs|临时利用情况', 'tdjb|土地级别', 'lmj|楼面价', 'dmj|地面价', 'jzdjnh|基准地价内涵', 'pgj|资产评估价值', 'pgFs|资产价值评估方法', 'tdmqzt|土地目前情况', 'isNdzcpg|年度资产评估', 'sjCb|项目实际发生成本', 'zyzyZc|农用地征收和转用支出', 'cqbcazZc|拆迁补偿安置支出', 'tdshZc|土地收回支出', 'tdsgZc|土地收购支出', 'yxgmZc|优先购买支出', 'qtZc|其他有关支出', 'tdqqKfcb|土地前期开发成本', 'zjCb|资金成本', 'qtCb|其他成本']},

        // {'yscdkData': ['tCbGhyt|土地规划情况']},
    ];*/

    decorators = ['tCbSczDk', 'yscdkData'];

    /**
     * @description 根据operateInfo存储的字符串进行页面用户看得懂的中文翻译
     * @param json
     * @demo
     * var json = {
            'className': 'nccdk',
            'changeContent': [{'dwMc': '宁波储备机构变成温州B储备机构'}, {'zdBh': '1110504135变成140104130124'}]
        };
     * @PS 注意数据有3种格式“修改成”/“删除变成空”/“新增”
     * @author xuhy 2019/07/18 21:39
     */
    private getCompareOpt(json: any): string {
        const validator = new Validator();

        // 最终返回的option_info
        let text: string = '';

        // JSON Reflect对象
        let obj: any;
        // JSON Reflect对象的英文名称如‘yscdkData’/‘tCbSczDk’
        let className = json.className;

        // 所有的中文处理首先放到这个数组里面
        let optArr: any[] = [];

        // 根据当前json的className值,去获取得到解析的对象,封装回到当前decorators数组[当前数组只会有一个符合条件的List]
        this.decorators = this.decorators.filter(decorator => decorator === className);
        if (validator.arrayNotEmpty(this.decorators)) {
            let decorator = this.decorators[0];
            if (decorator === 'yscdkData') {
                // console.log(Reflect.getMetadata(`${eName}`, new Tdcbdk(), `${eName}`));
                obj = new Tdcbdk();
            }
            json.changeContent.forEach(changeContentOne => {// [{zdBh: "201901020修改成1212"}]
                for (var key in changeContentOne) {// 英文名称Key
                    let val = changeContentOne[key]; // 值

                    let reflectName = Reflect.getMetadata(`${key}`, obj, `${key}`); // 注释中文
                    let cName = validator.isEmpty(reflectName) ? `${key}` : reflectName;// 中文名称是否为空判断,如果注释中文为空,则默认显示英文属性名称,如果不为空则显示中文属性名称
                    // console.log(`---KV参数,Ekey:${key},Ckey:${cName},val:${val}---`);

                    // 修改JSON判断
                    if (validator.contains(val, this.optionUpd)) {
                        if (!validator.isJSON(val.split(this.optionUpd)[0])) {
                            // 201901020不是JSON 单层处理
                            let st1 = val.replace(`${key}`, `${cName}`);
                            optArr.push(JSON.parse('{"' + cName + '":"' + st1 + '"}'));
                        } else {
                            let updJSONArrs = this.notJSONCompare(validator, `${val}`, this.optionUpd, obj);
                            updJSONArrs.forEach(e => {
                                optArr.push(e);
                            });
                        }
                    }
                }
            });
        }

        optArr.forEach((o: any, i: number) => {
            let o_text: string = JSON.stringify(o).substr(1, JSON.stringify(o).length - 2).replace('":"', '变更内容:');
            // 按照右侧格式显示[储备机构名称变更内容:宁波储备机构变成温州B储备机构,收储地块编号变更内容:1110504135变成140104130124。]
            if (i < optArr.length - 1) {
                o_text = o_text.substring(1, o_text.length - 1) + ',';
            } else {
                o_text = o_text.substring(1, o_text.length - 1) + '。';
            }
            text = text + o_text;
        });
        return text;
    }

    // change:修改成
    private notJSONCompare(validator, notJsonStr: string, change: string, obj: any) {
        let arr = [];

        if (validator.isNotEmpty(notJsonStr)) {
            if (notJsonStr.indexOf(change) >= 0) {
                let obj1 = notJsonStr.split(change)[0];
                let obj2 = notJsonStr.split(change)[1];

                for (var key in (JSON.parse(obj1))[0]) {
                    let beforeVal = (JSON.parse(obj1))[0][key];
                    let afterVal = (JSON.parse(obj2))[0][key];
                    if (beforeVal != afterVal) {
                        let reflectName = Reflect.getMetadata(`${key}`, obj, `${key}`); // 注释中文
                        let cName = validator.isEmpty(reflectName) ? `${key}` : reflectName;// 中文名称是否为空判断,如果注释中文为空,则默认显示英文属性名称,如果不为空则显示中文属性名称
                        console.log(`---参数,中文注释参数是否为空:${validator.isEmpty(reflectName)},中文注释:${reflectName},空则取原来的英文注释eName:${key}---`);

                        let str = '{"' + cName + '": "' + beforeVal + '修改成' + afterVal + '"}';
                        arr.push(str);
                    }
                }
            }
            return arr;
        }
    }

//--------------------------------------------compareOptStr---------------------------------------------------------

最终前端效果

json在线比较工具:https://www.sojson.com/jsondiff.html

总结:之前json比较看了这篇文章https://blog.csdn.net/wangjin890620/article/details/81011461,用了一下确实可以自己也改了一下,但是场景发现如果比较的字段少还行,如果字段很多80多并且要输出这个stringbuffer的时候就会报内存溢出,当然作者的sout都是好使的,百思不得其解了,同伴们说死循环和空指针判断问题,于是自己修改了一下!有问题还会再改自己的代码,这个还是初版,思路就是上一个json和下一个json比较后,再反过来比较一次,一共比较两次,这样就知道新增、修改、删除三个点的内容。

  • class-validator

新知识点:https://angular.cn/tutorial/toh-pt2#style-the-heroes 用于是否为空 是否是xx的各种判断 email  【npm install class-validator --save】

发布了206 篇原创文章 · 获赞 49 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/Sicily_winner/article/details/96587737
今日推荐