一个Json结构对比的Python小工具兼谈编程求解问题

  先上代码。

  jsondiff.py  

  

#_*_encoding:utf-8_*_

import argparse

import json
import sys

reload(sys)
sys.setdefaultencoding(‘utf-8‘)

def parseArgs():
    description = ‘This program is used to output the differences of keys of two json data.‘
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument(‘file‘, help=‘Given file containing two json data separated by a new line with three semicolons.‘)
    args = parser.parse_args()
    filename = args.file
    return filename 

def readFile(filename):
    content = ‘‘
    f = open(filename)
    for line in f:
        content += line.strip("\n")
    f.close()
    return content

def parseKeys(jsonobj):
    jsonkeys = list()
    addJsonKeys(jsonobj, jsonkeys, ‘‘)
    return jsonkeys 

def addJsonKeys(jsonobj, keys, prefix_key):
    if prefix_key != ‘‘:
        prefix_key = prefix_key+‘.‘
    if isinstance(jsonobj, list):
        addKeysIflist(jsonobj, keys, prefix_key)
    elif isinstance(jsonobj, dict):
        addKeysIfdict(jsonobj, keys, prefix_key)    

def addKeysIflist(jsonlist, keys, prefix_key):
    if len(jsonlist) > 0:
        addJsonKeys(jsonlist[0], keys, prefix_key)

def addKeysIfdict(jsonobj, keys, prefix_key):
    for (key, value) in jsonobj.iteritems():
        keys.append(prefix_key + key)
        addJsonKeys(value, keys, prefix_key+key)   

def diffKeys(json1, json2):
    keys1 = parseKeys(json1)
    keys2 = parseKeys(json2)
    keyset1 = set(keys1)
    keyset2 = set(keys2)
    return keyset1.difference(keyset2) 

def cmpArray(jsonArr1, jsonArr2, diff, prefix_key):
    ‘‘‘
       need to be improved
    ‘‘‘
    arrlen1 = len(jsonArr1)
    arrlen2 = len(jsonArr2)
    minlen = min(arrlen1, arrlen2)
    if arrlen1 != arrlen2:
        diff.append((prefix_key+‘.length‘, arrlen1, arrlen2))
    for i in range(0, minlen):
        diffDict(jsonArr1[i], jsonArr2[i], diff, prefix_key)

def cmpPrimitive(key, value1, value2, diff, prefix_key):

    if isinstance(value1,list) or isinstance(value1, dict)        or isinstance(value2, list) or isinstance(value2, dict):
       return

    if value1 != value2:
       diff.append((prefix_key + key, str(value1), str(value2))) 

def diffDict(json1, json2, diff, prefix_key):

    if prefix_key != ‘‘:
        prefix_key = prefix_key+‘.‘

    for (key, value) in json1.iteritems():
        json2Value = json2.get(key)
        #print "key: ", key, ", value: ", value, " , value2: ", json2Value

        if json2Value == None:
            diff.append((prefix_key + key, value, None))

        if isinstance(value, dict) and isinstance(json2Value, dict):
            diffDict(value, json2Value, diff, prefix_key + key)        

        if isinstance(value, list) and isinstance(json2Value, list):
            cmpArray(value, json2Value, diff, prefix_key + key)   

        cmpPrimitive(key, value, json2Value, diff, prefix_key)    

def diffJson(json1, json2):
    jsondiff = list()
    diffDict(json1, json2, jsondiff, ‘‘)
    return jsondiff

def diffJsonToFile(filename, json1, json2):
    f_res = open(filename, ‘w‘)
    diff_res = diffJson(json1, json2)
    for diff in diff_res:
        (key,v1,v2) = diff
        if v2 is None:
            f_res.write(‘key %s in json1 not in json2. \n‘ % key)
        else:
            f_res.write(‘key %s in json1 = %s yet in json2 = %s. \n‘ %(key, v1, v2))

    f_res.close()

def tesParsetKeysSingle(jsonobj, expected):
    assert set(parseKeys(jsonobj)) == set(expected)

def testParseKeys():
    for v in ({}, [], "good", 1, 3.14, -2.71, -1, 0.1, 2.71E3, 2.71E+3, 2.71E-32, 2.71e3, 2.71e+3, 2.71e-32, True, False, None, "null\n\\\"\/\b\f\n\r\t\u"):
        tesParsetKeysSingle(parseKeys(v), [])
    tesParsetKeysSingle({"code": 200}, [‘code‘])
    tesParsetKeysSingle({"code": 200, "msg": "ok", "list": [], "extra":{}}, [‘code‘, ‘msg‘, ‘list‘, ‘extra‘])
    tesParsetKeysSingle({"code": 200, "msg": "ok", "list": [{"id": 20, "no":"115"}], "extra":{"size": 20, "info": {"owner": "qin"}}}, [‘code‘, ‘msg‘, ‘list‘, ‘list..id‘, ‘list..no‘, ‘extra‘, ‘extra.size‘, ‘extra.info‘, ‘extra.info.owner‘])
    tesParsetKeysSingle({‘msg‘: ‘ok‘, ‘code‘: 200, ‘list‘: [{‘items‘: [{‘price‘: 21, ‘infos‘: {‘feature‘: ‘‘}, ‘name‘: ‘n1‘}], ‘id‘: 20, ‘no‘: ‘1000020‘}], ‘metainfo‘: {‘total‘: 20, ‘info‘: {‘owner‘: ‘qinshu‘, ‘parts‘: [{‘count‘: 13, ‘time‘: {‘start‘: 1230002456, ‘end‘: 234001234}}]}}}, [‘msg‘, ‘code‘, ‘list‘, ‘list..items‘, ‘list..items..price‘, ‘list..items..infos‘, ‘list..items..infos.feature‘, ‘list..items..name‘,‘list..id‘, ‘list..no‘,  ‘metainfo‘, ‘metainfo.total‘, ‘metainfo.info‘, ‘metainfo.info.owner‘, ‘metainfo.info.parts‘, ‘metainfo.info.parts..count‘, ‘metainfo.info.parts..time‘ ,‘metainfo.info.parts..time.start‘, ‘metainfo.info.parts..time.end‘]) 

    print ‘testPassed‘

def test():
    testParseKeys()

if __name__ == "__main__":

    test()

    filename = parseArgs()
    content = readFile(filename)
    content1 = content.split(‘;;;‘)[0]
    content2 = content.split(‘;;;‘)[1]
    json1 = json.loads(content1)
    json2 = json.loads(content2)

    print "keys in json_data_v2: "
    print parseKeys(json2)

    print ‘keys in json_data_v1 yet not in json_data_v2: ‘
    print diffKeys(json1, json2)

    print ‘keys in json_data_v2 yet not in json_data_v1: ‘
    print diffKeys(json2, json1)

Json 测试数据:

{
    "code": 200,
    "msg": "ok",
    "list": [
        {
            "id": 20,
            "no": "1000020",
            "items": [
                {
                    "name": "n1",
                    "price": 21,
                    "infos": {
                        "feature": ""
                    }
                }
            ]
        }
    ],
    "metainfo": {
        "total": 20,
        "info": {
            "owner": "qinshu",
            "parts": [
                {
                    "count": 13,
                    "time": {
                        "start": 1230002456,
                        "end": 234001234
                    }
                }
            ]
        }
    }
}

;;; 

{
    "code": 200,
    "msg": "ok",
    "success": true,
    "list": [
        {
            "id": 22,
            "no": "1000020",
            "items": [
                {
                    "name": "n1",
                    "price": 21,
                    "comment": "very nice",
                    "infos": {
                        "feature": ""
                    }
                },
                {
                    "name": "n2",
                    "price": 22,
                    "comment": "good",
                    "infos": {
                        "feature": "small"
                    }
                }
            ]
        }
    ],
    "metainfo": {
        "total": 20,
        "info": {
            "owner": "qinshu",
            "parts": [
                {
                    "count": 15,
                    "range": {
                        "start": 1230003456,
                        "end": 234007890
                    }
                }
            ]
        }
    }
}

 使用:

  将要比较的两个 json 串拷贝到一个文本文件 json_data.txt 里,并使用一个 ;;; 的行隔开; 然后运行 python jsondiff.py json_data.txt

目前主要是能够比较 json 的结构, 即输出 json 串相异的 key 的集合。

  编程求解问题

  确定问题与求解方向  ->  结构解析与递归  ->  算法设计  ->  编程与测试  ->  总结

  确定问题与求解方向 

  首先确定一个合适的问题, 一个合适的求解方向。在 json 串对比的问题域中, 可以有两个目标: 1.  比较两个 json 串的结构的不同; 常常用于 API 变更后的兼容; 2.  比较两个 json 串的结构及值的差异。 第二个目标由于有数组的存在,而变得比较复杂。 鉴于目标一比较常用,可以先实现。

  结构解析与递归

  其次,要确定处理问题所涉及的对象结构。要解析复杂的结构, 通常也会涉及到递归求解。可以使用递归求解的问题特征是: 1.  对象结构是一个组合结构,该结构可以通过一个原子量与一个更小的同型结构组合而成; 2.  问题的解结构可以通过原子量的解结构与更小规模的同型结构的解结构组合而成; 3.  原子量的解是可行的。

  几乎所有常用的数据结构都是递归的。一个数值可以分解为两个数值之和; 一个字符串可以分解为一个字符与一个子字符串的连接;一个列表、链表、栈、队列均可以由列首或列尾元素与剩余元素组合而成; 一棵树可以通过根节点与其左右子树组合而成;一个图可以通过其分割的子图构成。无处不可递归。不过递归需要注意的一点是: 在子问题的解结构组合成原问题的解结构的时候,最好不存在解结构之间的顺序关系。也就是说,原问题的解结构是一个无序集合,只要子问题的解结构也是无序集合,那么就尽可以将子问题的解集合添加到原问题的解结构中;如果存在顺序关系,则在算法设计中要尤其注意确保这种顺序。

  算法设计

  理解了所要处理的结构,就可以进行算法设计了。 JSON 串有很明显的递归特性, 因此适合用递归来求解。Json 结构定义参见 http://www.json.org/ 。对于 Json 串的处理,可以分为三种情况: (1) 原子量的处理,比如数值、字符串、布尔值; (2)  映射的处理,遍历每个 key-value 对, 如果 value 是映射,那么就递归使用(2);如果 value 是数组,则使用 (3);  3. 列表的处理, 遍历每一个元素,若元素是映射,则使用(2) 处理;若元素是数组,则使用(3)处理。具体见程序。

  编程测试

  设计好算法,就可以开始愉快地编程啦! 编程可以使用意图导航编程, 首先编写出几个空函数, 表达对问题求解的步骤,然后完善每个函数,必要时修改其接口定义。 编程完成后需要使用覆盖性的测试来尽早检测出 bug ,  修复程序隐藏的错误, 提高程序的质量。

  话说,富有经验的程序员会花费更多时间在算法设计上,确保其可扩展性和完善性;算法设计也是更考验程序员的思维能力,无需电脑就可进行; 而编程则是一种更实际的乐趣。

  一点技巧

  在递归求解中, 如何构造最终的解结构是个问题。一个较简单的办法是,构造一个空列表,然后在递归的过程中,在空列表中添加子解。通常有一个主递归函数, 比如程序中的  addJsonKeys , 用于控制子结构的流程跳转; 而处理子结构的分函数 addKeysIflist , addKeysIfdict 可递归跳转到该主函数。在主递归函数最外层有一个调用者,用于设置主递归函数的初始值,比如空列表的解结构、其他的初始值。

总结记录

  总结与记录也是必不可少的。回顾一下,在完成该问题的求解过程中,遇到了什么问题, 收获了怎样的技法呢? 无论多小都值得记录,积微至著;尤其是一些不引人注意的"编程微思想"。其实只要是编程问题,核心总是"数据结构+算法"。 即使在应用编程中, 其实也是"数据结构+算法"的引申。"数据结构" 变成了应用中的 "数据库+缓存", "算法" 变成了 "流程+规则",所做的需求开发,也就是在 "数据库+缓存" 的数据背景下,设计和规划 "流程和规则", 以适应产品和业务的发展需求。

时间: 2024-10-13 23:28:11

一个Json结构对比的Python小工具兼谈编程求解问题的相关文章

Python 小工具集合

PyTools Python小工具的集合,工具彼此间无联系.基于Python 3.4. Github 地址: https://github.com/ChenZhongPu/PyTools 目前实现了: 查看新闻 查看微博 发布微博 搜索1024网站 Usage 查看新闻 python3 App.py news 使用腾讯新闻的RSS源. 查看微博 python3 App.py weibo 使用了Yahoo pipes.你需要得到要查看用户的微博ID, "` Hanhan's weibo accou

有哪些你不知道的Python小工具

python作为越来越流行的一种编程语言,不仅仅是因为它语言简单,有许多现成的包可以直接调用. python中还有大量的小工具,让你的python工作更有效率. 1. 快速共享 HTTP服务器 SimpleHTTPServer是python内置的web服务器,使用8000端口和HTTP协议共享. 能够在任意平台(Window,Linux,MacOS)快速搭建一个HTTP服务和共享服务,只需要搭建好python环境. python2版本: python -m SimpleHTTPServer py

发布一个从迅雷下载字幕的小工具

最近下载个电影找不到字幕,最后却用迅雷看看匹配上了.再接着试了一下,发现迅雷的字幕库还很全的.由于我平时很少用迅雷看看这个视频软件,虽然迅雷看看可以保存字幕,但用起来也还是非常不方便,便想找一个有没有独立的工具可以下载迅雷字幕库的字幕. 在寻找的过程中,发现不但有这种工具thunder-subtitle,就连算法都有了,其基本过程如下: 首先,取视频的一部分内容求sha哈希值,具体取内容的算法为: 如果文件小于60k(0x000),其文件所有内容 如果文件大于60k,取如下三部分的内容 开始20

自己DIY出来一个JSON结构化展示器

说来也巧,这个玩意,一直都想亲手写一个,因为一直用着各种网上提供的工具,觉得这个还是有些用途,毕竟,后面的实现思路和原理不是太复杂,就是对json的遍历,然后给予不同节点类型以不同的展现风格. 我这次,是出于将一个专利写清楚,自己构思了一个实现方案,且还能显示出当前的路径,具体的显示风格,依据自己的喜好,随便DIY吧. 写这个JSON展示器,其实有很多用处,不仅仅就是为了看一个json的结构化展示. 1. 更重要的是可以辅助用户和json数据进行交互,能够知道用户感兴趣的json字段是什么,可以

分享一个很早之前写的小工具DtSpyPlus

几年前写的一个获取windows窗体基本信息和屏幕取色的小工具 ,一直在用. 下载地址 http://files.cnblogs.com/dint/SpyPlus.zip

python小工具:用python操作HP的Quality Center

背景是这样的: 这个组的测试人员每跑一个case都要上传测试结果附件到QC.每个待测功能模块可能包含几十上百的case.于是手工上传测试结果变成了繁重的体力劳动.令人惊讶的是我们的工具开发组竟然说做不了QC的测试结果附件上传.更让我惊讶的是,测试人员竟然真的手工上传结果上传了大半年了. 以下我写了个小工具解决这个问题: 思路很简单,调用hp提供的ALM Rest api接口.把一个个用户操作转化成http请求.然后按照接口要求,把附件一个一个上传到指定的QC test instance上. 主要

三天教会大家做一个社保管理系统中的核销小工具

我想通过这个小工具,和大家一起从零搭架子,一起编写码,一起探讨一种编码风格,一种架构,一种编程的思想体系,嗯,有了思想体系,则大巧不工! 体验地址是http://tool.hrinto.cn/用户名和密码都是tengfei12345开源地址是https://github.com/Langbencom/HeXiao 下面截图几张 咱们言归正传. 先从搭架子开始 架构没有银弹,但有套路.我认为要吃透三层架构,其他复杂的也是基于现实业务场景由此改编而来.万变不离其宗. 创业三年了,开发的项目很多,就这

分享一个刷网页PV的python小脚本

下面分享一个小脚本,用来刷网页PV. [[email protected] ~]# cat www.py #!/usr/bin/python# coding: UTF-8import webbrowser as webimport timeimport osimport randomdata = raw_input('请输入网址:')count = random.randint (3,5)j = 0while j <count: i = 0 while i <= 3: web.open_new

python小工具使用笔记

1.pip pip是Python官方推荐的包管理工具,在doc界面直接使用pip或者pip3命令即可,例如安装gensim: C:\Users\kayan.sjc>pip3 install --upgrade gensim 2.python2代码转换python3工具2to3.py python3不兼容python2,有时候需要将python2的代码转换为python3的,可以使用Python 3自带了一个叫做2to3的实用脚本,脚本文件在python安装目录下\Tools\scripts中.例