समान क्रम में समान तत्वों वाले दो JSON ऑब्जेक्ट्स की तुलना कैसे करें?


101

सूचियों के क्रम की अवहेलना करते हुए मैं कैसे जांच कर सकता हूं कि क्या दो JSON ऑब्जेक्ट्स अजगर में बराबर हैं?

उदाहरण के लिए ...

JSON दस्तावेज़ एक :

{
    "errors": [
        {"error": "invalid", "field": "email"},
        {"error": "required", "field": "name"}
    ],
    "success": false
}

JSON दस्तावेज़ b :

{
    "success": false,
    "errors": [
        {"error": "required", "field": "name"},
        {"error": "invalid", "field": "email"}
    ]
}

aऔर bसमान की तुलना करनी चाहिए, भले ही "errors"सूचियों का क्रम अलग हो।


2
का डुप्लीकेट stackoverflow.com/questions/11141644/...
user2085282

1
क्यों न केवल उन्हें डिकोड किया जाए और तुलना की जाए? या क्या आप का अर्थ है "ऐरे" या listतत्वों का क्रम भी मायने नहीं रखता है?
mgilson

@ user2085282 इस प्रश्न पर एक अलग समस्या चल रही है।
user193661

2
कृपया मेरी भोली को क्षमा करें, लेकिन क्यों? सूची तत्वों में एक कारण के लिए एक विशिष्ट क्रम है।
ATOzTOA

1
जैसा कि इस उत्तर में कहा गया है, एक JSON सरणी को क्रमबद्ध किया जाता है, इसलिए विभिन्न प्रकार के आदेशों वाले सरणियों में ये वस्तुएं सख्त अर्थों में समान नहीं होंगी। stackoverflow.com/a/7214312/18891
एरिक नेस

जवाबों:


143

यदि आप एक ही तत्व के साथ दो वस्तुओं को चाहते हैं, लेकिन समान की तुलना करने के लिए एक अलग क्रम में है, तो स्पष्ट बात यह है कि उनमें से छांटी गई प्रतियों की तुलना करें - उदाहरण के लिए, आपके JSON स्ट्रिंग्स द्वारा दर्शाए गए शब्दकोशों के लिए aऔर b:

import json

a = json.loads("""
{
    "errors": [
        {"error": "invalid", "field": "email"},
        {"error": "required", "field": "name"}
    ],
    "success": false
}
""")

b = json.loads("""
{
    "success": false,
    "errors": [
        {"error": "required", "field": "name"},
        {"error": "invalid", "field": "email"}
    ]
}
""")
>>> sorted(a.items()) == sorted(b.items())
False

... लेकिन यह काम नहीं करता है, क्योंकि प्रत्येक मामले में, "errors"शीर्ष-स्तरीय तानाशाह की वस्तु एक अलग क्रम में समान तत्वों के साथ एक सूची है, और sorted()"शीर्ष" स्तर के अलावा कुछ भी सॉर्ट करने की कोशिश नहीं करती है एक चलने योग्य।

इसे ठीक करने के लिए, हम एक ऐसे orderedफंक्शन को परिभाषित कर सकते हैं जो किसी भी लिस्ट को खोजने के लिए पुन: क्रमबद्ध करेगा (और शब्दकोशों को (key, value)जोड़े की सूचियों में परिवर्तित कर सकता है ताकि वे ऑर्डर कर सकें):

def ordered(obj):
    if isinstance(obj, dict):
        return sorted((k, ordered(v)) for k, v in obj.items())
    if isinstance(obj, list):
        return sorted(ordered(x) for x in obj)
    else:
        return obj

यदि हम इस फ़ंक्शन को लागू करते हैं aऔर b, परिणाम बराबर हैं:

>>> ordered(a) == ordered(b)
True

1
बहुत बहुत धन्यवाद जीरो Piraeus। यह बिल्कुल सामान्य मल्लाह है जो मुझे चाहिए। लेकिन समस्या केवल यह है कि कोड केवल अजगर के लिए काम करता है 2. python3 के लिए नहीं। मुझे निम्न त्रुटि मिलती है: TypeError: unorderable type: dict () <dict () वैसे भी समाधान अब स्पष्ट है। मैं इसे python3 के लिए काम करने की कोशिश करूंगा। बहुत बहुत धन्यवाद

1
@ हस्सामहस्सम का अर्थ है कि मैं इसे ठीक करने के लिए पायथन 3.x के साथ काम करता हूं जब आपने पहली बार अनियंत्रित डाइक्स समस्या का उल्लेख किया था, लेकिन किसी तरह यह मुझसे दूर हो गया। यह अब 2.x और 3.x दोनों में काम करता है :-)
जीरो पीरियस

जब वहाँ एक सूची की तरह है ['astr', {'adict': 'something'}], मैं TypeErrorउन्हें सॉर्ट करने की कोशिश कर रहा है।
झेनक्सिआओ हाओ

1
@ Blairg23 आपने इस प्रश्न को गलत समझा है, जो JSON ऑब्जेक्ट्स की तुलना करने के बारे में है जब उनके पास सूची होती है जिनके तत्व समान होते हैं, लेकिन किसी भिन्न क्रम में, शब्दकोशों के किसी भी क्रम के बारे में नहीं
जीरो पिरियस

1
@ Blairg23 मैं मानता हूं कि प्रश्न अधिक स्पष्ट रूप से लिखा जा सकता है (हालांकि यदि आप संपादन इतिहास को देखते हैं , तो यह शुरू होने से बेहतर है)। पुन: शब्दकोशों और आदेश - हाँ, मुझे पता है ;-)
शून्य पीरियस

45

json.dumps(X, sort_keys=True)विकल्प का उपयोग करने का एक और तरीका हो सकता है :

import json
a, b = json.dumps(a, sort_keys=True), json.dumps(b, sort_keys=True)
a == b # a normal string comparison

यह नेस्टेड शब्दकोशों और सूचियों के लिए काम करता है।


{"error":"a"}, {"error":"b"}बनाम {"error":"b"}, {"error":"a"} यह बाद के मामले को पहले मामले में सुलझाने में सक्षम नहीं होगा
क्रोमहर्ट्स

@ ब्लेयरग 23 लेकिन अगर आपके पास सूची में नेस्टेड है तो आप क्या करेंगे? आप केवल शीर्ष-स्तरीय तानाशाही की तुलना नहीं कर सकते हैं और इसे एक दिन कह सकते हैं, यह वह नहीं है जो यह प्रश्न है।
stpk

4
यदि आपके पास सूचियाँ हैं तो यह काम नहीं करता है। जैसे json.dumps({'foo': [3, 1, 2]}, sort_keys=True) == json.dumps({'foo': [2, 1, 3]}, sort_keys=True)
डेनियल

7
@Danil और शायद यह नहीं होना चाहिए। सूचियाँ एक आदेशित संरचना हैं और यदि वे केवल क्रम में भिन्न हैं, तो हमें उन्हें अलग मानना ​​चाहिए। हो सकता है कि आपके usecase के लिए ऑर्डर मायने नहीं रखता हो, लेकिन हमें ऐसा नहीं मानना ​​चाहिए।
stpk

क्योंकि सूचियों को अनुक्रमणिका द्वारा आदेशित किया जाता है, उनका सहारा नहीं लिया जाएगा। [०, १] अधिकांश स्थितियों में [१, ०] के बराबर नहीं होना चाहिए। तो यह सामान्य मामले के लिए एक अच्छा समाधान है, लेकिन उपरोक्त प्रश्न के लिए नहीं। अभी भी +1
हैरिसन

18

उन्हें डिकोड करें और उनकी तुलना mgilson टिप्पणी के रूप में करें।

जब तक कुंजियाँ और मान मेल खाते हैं तब तक शब्दकोश के लिए आदेश मायने नहीं रखता। (पायथन में शब्दकोश का कोई आदेश नहीं है)

>>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1}
True

लेकिन सूची में आदेश महत्वपूर्ण है; सॉर्टिंग सूचियों के लिए समस्या का समाधान करेगी।

>>> [1, 2] == [2, 1]
False
>>> [1, 2] == sorted([2, 1])
True

>>> a = '{"errors": [{"error": "invalid", "field": "email"}, {"error": "required", "field": "name"}], "success": false}'
>>> b = '{"errors": [{"error": "required", "field": "name"}, {"error": "invalid", "field": "email"}], "success": false}'
>>> a, b = json.loads(a), json.loads(b)
>>> a['errors'].sort()
>>> b['errors'].sort()
>>> a == b
True

उपरोक्त उदाहरण प्रश्न में JSON के लिए काम करेगा। सामान्य समाधान के लिए, ज़ीरो पीरियस का उत्तर देखें।


2

निम्नलिखित दो dicts 'dictWithListsInValue' और 'reorderedDictWithReorderedListsInValue' के लिए, जो केवल एक दूसरे के पुन: क्रमबद्ध संस्करण हैं

dictObj = {"foo": "bar", "john": "doe"}
reorderedDictObj = {"john": "doe", "foo": "bar"}
dictObj2 = {"abc": "def"}
dictWithListsInValue = {'A': [{'X': [dictObj2, dictObj]}, {'Y': 2}], 'B': dictObj2}
reorderedDictWithReorderedListsInValue = {'B': dictObj2, 'A': [{'Y': 2}, {'X': [reorderedDictObj, dictObj2]}]}
a = {"L": "M", "N": dictWithListsInValue}
b = {"L": "M", "N": reorderedDictWithReorderedListsInValue}

print(sorted(a.items()) == sorted(b.items()))  # gives false

मुझे गलत परिणाम दिया अर्थात असत्य।

तो मैंने इस तरह से अपना खुद का cutstom ObjectComparator बनाया:

def my_list_cmp(list1, list2):
    if (list1.__len__() != list2.__len__()):
        return False

    for l in list1:
        found = False
        for m in list2:
            res = my_obj_cmp(l, m)
            if (res):
                found = True
                break

        if (not found):
            return False

    return True


def my_obj_cmp(obj1, obj2):
    if isinstance(obj1, list):
        if (not isinstance(obj2, list)):
            return False
        return my_list_cmp(obj1, obj2)
    elif (isinstance(obj1, dict)):
        if (not isinstance(obj2, dict)):
            return False
        exp = set(obj2.keys()) == set(obj1.keys())
        if (not exp):
            # print(obj1.keys(), obj2.keys())
            return False
        for k in obj1.keys():
            val1 = obj1.get(k)
            val2 = obj2.get(k)
            if isinstance(val1, list):
                if (not my_list_cmp(val1, val2)):
                    return False
            elif isinstance(val1, dict):
                if (not my_obj_cmp(val1, val2)):
                    return False
            else:
                if val2 != val1:
                    return False
    else:
        return obj1 == obj2

    return True


dictObj = {"foo": "bar", "john": "doe"}
reorderedDictObj = {"john": "doe", "foo": "bar"}
dictObj2 = {"abc": "def"}
dictWithListsInValue = {'A': [{'X': [dictObj2, dictObj]}, {'Y': 2}], 'B': dictObj2}
reorderedDictWithReorderedListsInValue = {'B': dictObj2, 'A': [{'Y': 2}, {'X': [reorderedDictObj, dictObj2]}]}
a = {"L": "M", "N": dictWithListsInValue}
b = {"L": "M", "N": reorderedDictWithReorderedListsInValue}

print(my_obj_cmp(a, b))  # gives true

जो मुझे सही उम्मीद आउटपुट दिया!

तर्क बहुत सरल है:

यदि ऑब्जेक्ट टाइप 'सूची' के हैं, तो पहली सूची के प्रत्येक आइटम की दूसरी सूची के आइटमों से तुलना करें जब तक कि पाया नहीं जाता है, और यदि आइटम दूसरी सूची से गुजरने के बाद नहीं मिला है, तो 'पाया' = गलत होगा। 'पाया' मान लौटा है

और यदि वस्तुओं की तुलना की जाए तो वे 'प्रकार' हैं, तो दोनों वस्तुओं में सभी संबंधित कुंजियों के लिए मौजूद मानों की तुलना करें। (पुनरावर्ती तुलना की जाती है)

और केवल बस obj1 == obj2 पर कॉल करें। यह डिफ़ॉल्ट रूप से स्ट्रिंग्स और संख्याओं की वस्तु के लिए ठीक काम करता है और उन eq () के लिए उचित रूप से परिभाषित किया गया है।

(ध्यान दें कि ऑब्जेक्ट 2 में पाई गई वस्तुओं को हटाकर एल्गोरिथ्म को और बेहतर बनाया जा सकता है, ताकि ऑब्जेक्ट 1 के अगले आइटम ऑब्जेक्ट 2 में पहले से मौजूद वस्तुओं के साथ खुद की तुलना न करें)


क्या आप कृपया अपने कोड के इंडेंटेशन को ठीक कर सकते हैं ?
कोलीड्रे

@ एकीकरण क्या अब इंडेंटेशन ठीक है?
निकविविज

नहीं, अभी भी वहाँ जारी करता है। फ़ंक्शन हेड के बाद, ब्लॉक को भी इंडेंट करना पड़ता है।
कोलाइडर

हाँ। मैंने एक बार फिर से संपादित किया। मैंने इसे आईडीई में चिपकाया, और यह अब काम कर रहा है।
निकविवि सिप 30'18

1

आप अपने खुद के बराबर कार्य लिख सकते हैं:

  • dicts बराबर हैं यदि: 1) सभी कुंजियाँ समान हैं, 2) सभी मान समान हैं
  • सूचियाँ समान हैं यदि: सभी वस्तुएँ समान और समान क्रम में हों
  • आदिम समान हैं अगर a == b

: क्योंकि आप json के साथ काम कर रहे हैं, आप मानक अजगर प्रकार होगा dict, list,, आदि ताकि आप कठिन प्रकार की जाँच कर सकते हैं if type(obj) == 'dict':, आदि

पर्याप्त उदाहरण (परीक्षण नहीं):

def json_equals(jsonA, jsonB):
    if type(jsonA) != type(jsonB):
        # not equal
        return False
    if type(jsonA) == dict:
        if len(jsonA) != len(jsonB):
            return False
        for keyA in jsonA:
            if keyA not in jsonB or not json_equal(jsonA[keyA], jsonB[keyA]):
                return False
    elif type(jsonA) == list:
        if len(jsonA) != len(jsonB):
            return False
        for itemA, itemB in zip(jsonA, jsonB):
            if not json_equal(itemA, itemB):
                return False
    else:
        return jsonA == jsonB

0

दूसरों के लिए जो दो JSON ऑब्जेक्ट्स (आमतौर पर, एक संदर्भ और एक लक्ष्य है ) को डिबग करना चाहते हैं , यहां एक समाधान है जिसका आप उपयोग कर सकते हैं। यह लक्ष्य से संदर्भ तक विभिन्न / बेमेल लोगों के " पथ " को सूचीबद्ध करेगा ।

level विकल्प का उपयोग यह चुनने के लिए किया जाता है कि आप कितने गहरे में देखना चाहते हैं।

show_variables विकल्प को संबंधित चर दिखाने के लिए चालू किया जा सकता है।

def compareJson(example_json, target_json, level=-1, show_variables=False):
  _different_variables = _parseJSON(example_json, target_json, level=level, show_variables=show_variables)
  return len(_different_variables) == 0, _different_variables

def _parseJSON(reference, target, path=[], level=-1, show_variables=False):  
  if level > 0 and len(path) == level:
    return []
  
  _different_variables = list()
  # the case that the inputs is a dict (i.e. json dict)  
  if isinstance(reference, dict):
    for _key in reference:      
      _path = path+[_key]
      try:
        _different_variables += _parseJSON(reference[_key], target[_key], _path, level, show_variables)
      except KeyError:
        _record = ''.join(['[%s]'%str(p) for p in _path])
        if show_variables:
          _record += ': %s <--> MISSING!!'%str(reference[_key])
        _different_variables.append(_record)
  # the case that the inputs is a list/tuple
  elif isinstance(reference, list) or isinstance(reference, tuple):
    for index, v in enumerate(reference):
      _path = path+[index]
      try:
        _target_v = target[index]
        _different_variables += _parseJSON(v, _target_v, _path, level, show_variables)
      except IndexError:
        _record = ''.join(['[%s]'%str(p) for p in _path])
        if show_variables:
          _record += ': %s <--> MISSING!!'%str(v)
        _different_variables.append(_record)
  # the actual comparison about the value, if they are not the same, record it
  elif reference != target:
    _record = ''.join(['[%s]'%str(p) for p in path])
    if show_variables:
      _record += ': %s <--> %s'%(str(reference), str(target))
    _different_variables.append(_record)

  return _different_variables
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.