【问题标题】:Is there a good, controllable way to compare two .json files and generate their difference into an Excel Sheet in Python有没有一种好的、可控的方法来比较两个 .json 文件并将它们的差异生成到 Python 中的 Excel 工作表中
【发布时间】:2021-10-10 21:04:52
【问题描述】:

我曾尝试使用 jsondiff,但输出不符合我的要求。

with open('C/abc/file1.json') as f:
        a= json.load(f)
    
    with open('C/abc/file2.json') as s:
        b= json.load(s)
    c= diff(a,b)

我要在 Excel 表中打印的是 Delta,文件 1 中存在但文件 2 中没有的东西,文件 1 中存在但文件 2 中更改的东西,文件 2 中不存在的新添加的东西file1,如果可能的话,还有行号。 因此,如果有人知道如何实现这一点,请分享它,如果需要更多说明,请告诉我。

由于限制,我无法粘贴文件的原始内容,但这些文件中的内容看起来如何我已经放在下面了。

file1.json

    {
  "Indicator": {
    "key1": "value 1",
    "key2": "value 2",
    "Name": "value 3",
    "key4": "value 4",
    "Description": "some text",
    "Subformulas": {
      "some key": "some sub-formula"
    },
    "Formula": "some formula"
  }
}

file2.json

    {
  "Indicator": {
    "key1": "value 1",
    "key2": "value 2",
    "key3":"value changed",
    "Name": "value 3",
    "key4": "value 4",
    "Description": "some text",
    "Subformulas": {
      "some key": "change in some sub-formula"
    },
    "Formula": "some formula"
  }
}

我已经用它来打印 Excel 工作表中的差异。我在 Stack Overflow 中的一个问题的答案中找到了代码,但它没有将差异打印到 Excel 工作表中......在终端上它是显示差异,但在 excel 表中却不是。所以我认为我做错了什么,我不知道如何纠正。

c.items()
c1=list(c.items())
workbook = xlsxwriter.Workbook('myfile.xlsx')
worksheet = workbook.add_worksheet()
row = 0
col = 0

order=sorted(c.keys())

for key in order:
    row += 1
    #print(key)
    worksheet.write(row,    col,     key)
    for item in c[key]:
        #print(item,row, col+1)
        worksheet.write(row, col + 1, item)
        col += 1
    col = 0

workbook.close()

【问题讨论】:

  • json文件的结构是什么?你能发布数据样本吗?
  • 请求库/软件在 StackOverflow 上是特别离题的。展示你诚实的尝试,以获得你需要的输出作为你的问题中的代码 - 即不仅仅是你说的代码是不充分的。
  • difflib 在标准库中,所以我猜它仍然是主题
  • @RJAdriaansen 我已经更新了问题,抱歉,由于规定无法粘贴.json文件的原始内容,我只是删除了内容但格式相同。
  • @balmy 我想我并没有在这里问任何题外话,只是当我试图打印到 excel 表时我遇到了问题......我试图在任何地方对 xlsxwriter 进行研究但我不太明白如何使用它

标签: python json xlsxwriter delta


【解决方案1】:

如果您阅读 jsondiff https://github.com/xlwings/jsondiff/tree/05dd7dd6c0b712fe54491289d3972ab58a125e11/jsondiff 的源代码,您会看到结果的“语法”有几个选项 - 简短文档中没有提及。如您所见,默认值为“紧凑”,它似乎只显示更改的内容。如果您选择“显式”语法,则会在差异输出中获得“插入”、“更新”、“删除”等。您可以探索另一种语法“对称”。

所以改变:

c = jsondiff.diff(a,b)

到:

c = jsondiff.diff(a,b,syntax='explicit')

我修改了你的 file2,所以它添加和删除了一些键:

b = {
  "Indicator": {
    "key1": "value 1",
    "key3":"value changed",
    "Name": "value 3",
    "key4": "value 4",
    "key5": "added",
    
    "Description": "some text",
    "Subformulas": {
      "some key": "change in some sub-formula"
    },
    "Formula": "some formula"
  }
}

diff 的c 结果中的任何结果都是(pprint-ed):

{update: {'Indicator': {insert: {'key3': 'value changed', 'key5': 'added'},
                        delete: ['key2'],
                        update: {'Subformulas': {update: {'some key': 'change '
                                                                      'in some '
                                                                      'sub-formula'}}}}}}

xls 编写代码必须“遍历”嵌套字典以产生输出。

经过一番研究,我发现此代码用于通用列表/字典步行器 objwalk() https://code.activestate.com/recipes/577982-recursively-walk-python-objects/

我选择通过将键的名称与 / 连接来使键的路径变平 - 显然,如果您的键可能包含 /,您将不得不适应这一点。

这是生成的代码,作为一个最小的可重现示例 - 注意,当您下次在此处创建问题时,您应该对您输入问题的代码采用相同的方法,即提供 所有内容 需要运行代码。

import collections.abc
import jsondiff
import pprint

import xlsxwriter

a = {
  "Indicator": {
    "key1": "value 1",
    "key2": "value 2",
    "Name": "value 3",
    "key4": "value 4",
    "Description": "some text",
    "Subformulas": {
      "some key": "some sub-formula"
    },
    "Formula": "some formula"
  }
}

b = {
  "Indicator": {
    "key1": "value 1",
    "key3":"value changed",
    "Name": "value 3",
    "key4": "value 4",
    "key5": "added",
    
    "Description": "some text",
    "Subformulas": {
      "some key": "change in some sub-formula"
    },
    "Formula": "some formula"
  }
}

c = jsondiff.diff(a,b,syntax='explicit')

pprint.pprint( c)

# dual python 2/3 compatability, inspired by the "six" library
string_types = (str, unicode) if str is bytes else (str, bytes)
iteritems = lambda mapping: getattr(mapping, 'iteritems', mapping.items)()
# from https://code.activestate.com/recipes/577982-recursively-walk-python-objects/
def objwalk(obj, path=(), memo=None):
    if memo is None:
        memo = set()
    iterator = None
    if isinstance(obj, collections.abc.Mapping):
        iterator = iteritems
    elif isinstance(obj, (collections.abc.Sequence, collections.abc.Set)) and not isinstance(obj, string_types):
        iterator = enumerate
    if iterator:
        if id(obj) not in memo:
            memo.add(id(obj))
            for path_component, value in iterator(obj):
                for result in objwalk(value, path + (path_component,), memo):
                    yield result
            memo.remove(id(obj))
    else:
        yield path, obj
       
# from https://stackoverflow.com/a/5389547/2318649
def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return zip(*[iter(iterable)]*n)

workbook = xlsxwriter.Workbook('myfile.xlsx')
worksheet = workbook.add_worksheet()

row = 0
# write the column headings
worksheet.write(row, 0, "Path" )
worksheet.write(row, 1, "Change" )
worksheet.write(row, 2, "New Value")

for o in objwalk(c):
    print( f"{o=}" )
    keypath = []
    for op,key in grouped(o[0],2):
        if type(key)==str:
            keypath.append(str(key))
#        print( f"  {op=}" )
        print( f"  {op=} {key=}" )
        lastop = op
    print( f"{keypath=} {lastop=} {o[1]=}" )
    row += 1
    # write the values for this row
    worksheet.write(row, 0, "/".join(keypath) )
    worksheet.write(row, 1, repr(lastop))
    worksheet.write(row, 2, o[1])
    
workbook.close()

输出:

“删除”行与“新值”列中显示的已删除键看起来很奇怪,但这是jsondiffexplicit 语法生成其输出的方式。你当然可以用不同的方式处理它。

【讨论】:

  • 顺便说一句,这个解决方案运行良好,并且完美地完成了这项工作。仅出现此警告 DeprecationWarning: Using or importing the ABCs from 'collections' instead from 'collections.abc' 自 Python 3.3 起已弃用,在 3.9 中,如果 isinstance(obj, collections.Mapping): 将停止工作。我已经更新了所有的库,但仍然会出现这种情况。
  • 从下一次开始,我会把所有需要的代码都放上来......谢谢你指出这一点
  • 将所有 collections 更改为 collections.abc - 我已在答案中编辑了代码
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-28
  • 2020-07-25
  • 1970-01-01
  • 2021-08-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多