0

My goal is to merge following four Jsons into one:

'{}'

'{"A (1)": {"B (2)": {"C (3)": [{"fewfesfz": 48, "qwerty": 2}]}}}'

'{"A (1)": {"B (2)": {"D (3)": [{"blablabla": 1}]}}}'


'{"A (1)": {"E (2)": {"F (3)": [{"blablabla": 20}]}}}'

The requirements are:

  • It shall be written in Python 3.
  • Dictionary to far right: only values are allowed to be overwritten.
  • All dictionaries keys are NOT allowed to be overwritten nor deleted. This is also the keys for dictionaries far right. Keys are not allowed to be lost when merging two jsons.
  • When merging two dictionaries with same keys, then the values are appended/added.

The final result shall look like this:

{
"A (1)": {
    "B (2)": {
        "C (3)": [
            {
                "fewfesfz": 48,
                "qwerty": 2
            }
        ],
        "D (3)": [
            {
                "blablabla": 1
            }
        ]
    },
    "E (2)": {
        "F (3)": [
            {
                "blablabla": 20
            }
        ]
    }
}

}

My solution is the following:

import json

s = json.loads('{}')
x = json.loads('{"A (1)": {"B (2)": {"C (3)": [{"fewfesfz": 48, "qwerty": 2}]}}}')
y = json.loads('{"A (1)": {"B (2)": {"D (3)": [{"blablabla": 1}]}}}')
z = json.loads('{"A (1)": {"E (2)": {"F (3)": [{"blablabla": 20}]}}}')


def merge_json(json1, json2):
    try:
        json1['A (1)']['B (2)'].update(json2['A (1)']['B (2)'])
        return json1
    except KeyError:
        return {**json1, **json2}


result = merge_json(x, s)
result = merge_json(result, y)
result['A (1)'].update(z['A (1)'])
print(json.dumps(x, indent=4))

As you can see, it is very hard to manage long term. Specially, if the depths gets changed from 3 to 4 nodes or from 3 to 2 nodes. There is no way of knowing, in which order the big letters (A, B, C, D...) come. So far, I can only come up with extremely complex functions with lot of if statements to test if the nodes exists or not. Only then I know, if I can make full merge or need to call the update dictionary method.

Have anyone a better solution how to solve this problem, then I would be very grateful.

EDIT 1 (Added more examples below):

If the following two json are merged

json1 = '{"A (1)": {"B (2)": {"D (3)": [{"blablabla": 1}]}}}'
json2 = '{"A (1)": {"B (2)": {"D (3)": [{"blablabla": 2}]}}}'

Result would be:

'{"A (1)": {"B (2)": {"D (3)": [{"blablabla": 2}]}}}'

Only the value for the key "blablabla" changed from 1 to 2. This happens because it was processed later.

If following two json are merged:

json1 = '{"A (1)": {"B (2)": {"D (3)": [{"blablabla": 1}]}}}' 
json2 = '{"A (1)": {"D (3)": [{"blablabla": 1}]}}'

Result would be:

{'A (1)': {'B (2)': {'D (3)': [{'blablabla': 1}]}, 'D (3)': [{'blablabla': 1}]}}

The input structure will remain the same in output.

crystal
  • 37
  • 6
  • 1
    Are all final key/values at the same known level? What would be the output when input is `'{"A (1)": {"B (2)": {"D (3)": [{"blablabla": 1}]}}}'`,`'{"A (1)": {"B (2)": {"D (3)": [{"blablabla": 2}]}}}'`. And when input is `'{"A (1)": {"B (2)": {"D (3)": [{"blablabla": 1}]}}}'` , `'{"A (1)": {{"D (3)": [{"blablabla": 1}]}}'` – Max Feb 07 '22 at 18:49
  • It looks very promising! Will need to do more testing, but I think it is what I am looking for. Super! – crystal Feb 07 '22 at 19:16
  • https://mergedeep.readthedocs.io/en/latest/ – Max Feb 07 '22 at 19:27

1 Answers1

1

The real answer is here: https://stackoverflow.com/a/20666342/218663 but to demonstrate that you would get the results you seek try:

def merge(source, destination):
    for key, value in source.items():
        if isinstance(value, dict):
            merge(value, destination.setdefault(key, {}))
        else:
            destination[key] = value
    return destination

tests = [
    {
        "given": [
            {},
            {"A (1)": {"B (2)": {"C (3)": [{"fewfesfz": 48, "qwerty": 2}]}}},
            {"A (1)": {"B (2)": {"D (3)": [{"blablabla": 1}]}}},
            {"A (1)": {"E (2)": {"F (3)": [{"blablabla": 20}]}}}
        ],
        "expected": {
            "A (1)": {
                "B (2)": {
                    "C (3)": [{"fewfesfz": 48,"qwerty": 2}],
                    "D (3)": [{"blablabla": 1}]
                    },
                "E (2)": {
                    "F (3)": [{"blablabla": 20}]
                }
            }
        }
    },
    {
        "given": [
            {"A (1)": {"B (2)": {"D (3)": [{"blablabla": 1}]}}},
            {"A (1)": {"B (2)": {"D (3)": [{"blablabla": 2}]}}}
        ],
        "expected": {"A (1)": {"B (2)": {"D (3)": [{"blablabla": 2}]}}}
    },
    {
        "given": [
            {"A (1)": {"B (2)": {"D (3)": [{"blablabla": 1}]}}},
            {"A (1)": {"D (3)": [{"blablabla": 1}]}}
        ],
        "expected": {'A (1)': {'B (2)': {'D (3)': [{'blablabla': 1}]}, 'D (3)': [{'blablabla': 1}]}}
    }
]

for test in tests:
    result = {}
    for item in test["given"]:
        merge(item, result)
    print(result==test["expected"])

Should give you:

True
True
True

If this fits your needs, feel free to delete this question rather than accepting this as an answer :-)

JonSG
  • 10,542
  • 2
  • 25
  • 36
  • There's a bug in that code + mutability issue. Just uploaded a gist with fixes applied and used some of your test cases thanks =) https://gist.github.com/ton77v/c84ac717e51dad03fd155dafb77ef8f4 – ElRey777 Jul 21 '23 at 13:47