Сравнение словарей с большой вложенностью

(python) Задача такая: Имеется 2 словаря одинакового формата

{
  (dict)tag1:[
    (str)sub_tag1,
    (dict)sub_tag2: {[
                       sub_sub_tag1 ... (str/dict)
                    ]}
    (str)sub_tag3,
    (str)sub_tag4
    ...
    (str/dict)sub_tagN
  ]
  ...
  (str/dict)tagN
}

Важно вложенность может быть больше или меньше!

Каждый тег может быть строкой либо еще одним вложенным словарем при чем в словарях 1 и 2 выше по дереву вложенности может быть 2 одинаковых словаря ребенка но стоящих в разных местах в списке

Необходимо сделать функцию которая принимает на вход 2 словаря и отдает словарь изменений первого словаря относительно второго:

{
  tag10: +
  tag11: [
          sub_tag: +
          sub_tag: [
                    sub_sub_tag: -
                   ]
         ]
}

при этом нужно учитывать что словари и строки во вложенностях могут быть просто в разных местах

примеры словарей:

d1 
{'offer':
      ['type', 
      'property-type',
      'category',
      'mortgage',
      'deal-status',
      'creation-date',
      'last-update-date',
      {'location': 
            ['country',
            'locality-name',
            'address', 
            'latitude',
            'longitude',
            'apartment']},
      {'sales-agent':
            ['name',
            'phone',
            'category',
            'organization',
            'url',
            'email',
            'photo']}, 
      {'price': 
            ['value', 
            'currency']}, 
      'image',
      'image',
      'image',
      'image',
      'image',
      {'area': 
            ['value',
            'unit']}, 
      {'kitchen-space':
            ['value', 
            'unit']}, 
      'renovation', 
      'description', 
      'new-flat', 
      'floor', 
      'rooms', 
      'apartments', 
      'floors-total', 
      'building-name', 
      'yandex-building-id', 
      'yandex-house-id', 
      'building-state',
      'built-year', 
      'ready-quarter', 
      'building-section']
}


d2
{
'offer':[
      'type', 
      'property-type',
      'category',
      'mortgage', 
      'deal-status',
      'creation-date',
      'last-update-date', 
      {'location': 
            ['country', 
            'locality-name',
            'address',
            'latitude',
            'longitude',
            'apartment']},
      {'sales-agent':
            ['name',
            'phone',
            'category',
            'organization',
            'url',
            'email',
            'photo']},
      {'price': 
            ['value',
            'currency']}, 
      'image',
      'image',
      'image',
      'image',
      'image',
      'image',
      {'area': 
            ['value',
             'unit']}, 
      {'living-space': 
            ['value', 
            'unit']},
      {'kitchen-space': 
            ['value', 
            'unit']}, 
      'renovation',
      'description',
      'new-flat',
      'floor',
      'rooms',
      'apartments',
      'floors-total',
      'building-name', 
      'yandex-building-id',
      'yandex-house-id',
      'building-state',
      'built-year',
      'ready-quarter',
      'building-section']
}

для наглядности

d1 {offer  :  ['type', 'property-type', 'category', 'mortgage', 'deal-status', 'creation-date', 'last-update-date', {'location': ['country', 'locality-name', 'address', 'latitude', 'longitude', 'apartment']}, {'sales-agent': ['name', 'phone', 'category', 'organization', 'url', 'email', 'photo']}, {'price': ['value', 'currency']}, 'image', 'image', 'image', 'image', 'image', {'area': ['value', 'unit']}, {'kitchen-space': ['value', 'unit']}, 'renovation', 'description', 'new-flat', 'floor', 'rooms', 'apartments', 'floors-total', 'building-name', 'yandex-building-id', 'yandex-house-id', 'building-state', 'built-year', 'ready-quarter', 'building-section']}
d2 {offer  :  ['type', 'property-type', 'category', 'mortgage', 'deal-status', 'creation-date', 'last-update-date', {'location': ['country', 'locality-name', 'address', 'latitude', 'longitude', 'apartment']}, {'sales-agent': ['name', 'phone', 'category', 'organization', 'url', 'email', 'photo']}, {'price': ['value', 'currency']}, 'image', 'image', 'image', 'image', 'image', 'image', {'area': ['value', 'unit']}, {'living-space': ['value', 'unit']}, {'kitchen-space': ['value', 'unit']}, 'renovation', 'description', 'new-flat', 'floor', 'rooms', 'apartments', 'floors-total', 'building-name', 'yandex-building-id', 'yandex-house-id', 'building-state', 'built-year', 'ready-quarter', 'building-section']}
 

Я пока вижу рекурсивную функцию с 2 проходами сначала по первому словарю для определения новых тегов потом по второму словарю для определения тегов которых нет в первом (однако застрял на определении одинаковых словарей если они различаются только одним вложеным тегом например и находятся в разных местах в списке)

мой набросок

def findDiff1(dict_1, dict_2):
    changes = {}
    for key in dict_1:
        if key not in dict_2:
            changes[key] = '+'
        else:
            for item in dict_1[key]:
                if type(item) is dict:
                    # todo find dict in arr on key of dict 
                    if item in dict_2[key]:
                        findDiff1(item, dict_2[key] # dict)
                    else: 
                        changes[key] = {}
                        changes[key][item[0]]
                elif type(item) is str:
                    if item not in dict_2:
                        changes[key] = {}
                        changes[key][item] = '+'
    
    # next for on dict_2
    return changes

Ответы (1 шт):

Автор решения: strawdog

А вариант через готовые модули вас не устроит? что-то типа jsondiff? Как-нибудь так:

import json
import jsondiff
j1 = json.loads(str(d1).replace("'", '"'))
j2 = json.loads(str(d2).replace("'", '"'))
res = jsondiff.diff(j1, j2)

ну и res будет:

{'offer': {insert: [(10, 'image'), (17, {'living-space': ['value', 'unit']})]}}
# ключ, позиция, ключ или значение
→ Ссылка