দুটি JSON অবজেক্টকে কীভাবে একই উপাদানগুলির সাথে আলাদা আলাদা অর্ডারে তুলনা করব?


104

দুটি ক্রিয়াকলাপ জেএসওএন পদার্থ অজগরে সমান কিনা তা আমি কীভাবে পরীক্ষা করব?

উদাহরণ স্বরূপ ...

JSON নথিটি একটি :

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

জেএসওএন নথি :

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

aএবং তালিকার bক্রম পৃথক হওয়া সত্ত্বেও সমান তুলনা করা উচিত "errors"



4
কেন কেবল তাদের ডিকোড করে তুলনা করব না? বা আপনার অর্থ "অ্যারে" বা listউপাদানগুলির ক্রমটি কোনওভাবেই আসে না?
মিগিলসন

@ user2085282 এই প্রশ্নটির একটি পৃথক সমস্যা চলছে।
ব্যবহারকারী 193661

4
দয়া করে আমার নির্দোষকে ক্ষমা করবেন, তবে কেন? তালিকার উপাদানগুলির একটি কারণে নির্দিষ্ট আদেশ রয়েছে order
ATOzTOA

4
এই উত্তরে উল্লিখিত হিসাবে, একটি জেএসওএন অ্যারে বাছাই করা হয়েছে যাতে বিভিন্ন ধরণের অর্ডারযুক্ত অ্যারেযুক্ত এই বিষয়গুলি কঠোর অর্থে সমান হয় না। stackoverflow.com/a/7214312/18891
এরিক নেস

উত্তর:


146

আপনি একই উপাদানের সঙ্গে কিন্তু সমান, তারপর সুস্পষ্ট তাদের সাজানো কপি তুলনা করতে জিনিস তুলনা করার জন্য একটি বিভিন্ন অনুক্রমে দুটি বস্তুর চান - উদাহরণস্বরূপ অভিধান জন্য আপনার তাদেরকে 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

4
আপনাকে অনেক ধন্যবাদ জিরো পাইরেয়াস এটি ঠিক আমার সাধারণ সমাধান যা দরকার। তবে একমাত্র সমস্যা হ'ল কোডটি পাইথন ২.x এর জন্য কাজ করে না পাইথন ৩ এর জন্য নয়। আমি নিম্নলিখিত ত্রুটিটি পেয়েছি: TypeError: অকেজোযোগ্য প্রকারগুলি: ডিক () <ডিক () যাইহোক সমাধান এখন পরিষ্কার। আমি এটি অজগর 3 জন্য কাজ করার চেষ্টা করব। অনেক অনেক ধন্যবাদ

4
@ হৌসামএইচএসএম এর অর্থ আমি পাইথন ৩.x এর সাথে কাজ করার জন্য এটি ঠিক করেছিলাম যখন আপনি প্রথম অরক্ষিত ডিসটস সমস্যার কথা উল্লেখ করেছিলেন তবে কোনওভাবে এটি আমার থেকে দূরে সরে যায়। এটি এখন 2.x এবং 3.x :-) উভয় ক্ষেত্রেই কাজ করে
জিরো পাইরেয়াস

যখন একটি তালিকা আছে ['astr', {'adict': 'something'}], TypeErrorযখন আমি তাদের বাছাই করার চেষ্টা করেছি I
ঝেংসিওও হাও

4
@ ব্লেয়ারজ ২৩ আপনি প্রশ্নটি ভুল বুঝেছেন, যা জেএসওএন অবজেক্টকে সমান হিসাবে তুলনা করার বিষয়ে যখন এর তালিকাগুলি একই উপাদানগুলির সমান, তবে ভিন্ন ধারায়, অভিধানের কোনও অনুমিত অর্ডার সম্পর্কে নয়
জিরো

4
@ ব্লেয়ারজ ২৩ আমি সম্মত হই যে প্রশ্নটি আরও স্পষ্টভাবে লেখা যেতে পারে (যদিও আপনি সম্পাদনার ইতিহাসের দিকে তাকান , এটি শুরু হওয়ার চেয়ে ভাল)) পুনরায়: অভিধানগুলি এবং ক্রম - হ্যাঁ, আমি জানি ;-)
জিরো

46

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)
ডানিল

8
@ ডানিল এবং সম্ভবত এটি করা উচিত নয়। তালিকাগুলি একটি আদেশযুক্ত কাঠামো এবং সেগুলি যদি কেবল ক্রম অনুযায়ী পৃথক হয় তবে আমাদের এগুলি আলাদা বিবেচনা করা উচিত। হতে পারে আপনার ইউজকেসের জন্য অর্ডারটি কোনও ব্যাপার নয়, তবে আমাদের এটি অনুমান করা উচিত নয়।
stpk

কারণ সূচী অনুসারে তালিকাগুলি অর্ডার করা হয় সেগুলি পুনর্বাসন করা হবে না। [0, 1] বেশিরভাগ পরিস্থিতিতে [1, 0] এর সমান হওয়া উচিত নয়। সুতরাং এটি সাধারণ ক্ষেত্রে ভাল সমাধান, তবে উপরের প্রশ্নের জন্য নয়। এখনও +1
হ্যারিসন

18

এগুলি ডিকোড করুন এবং তাদের মিলিগ্রামের মন্তব্যের সাথে তুলনা করুন।

কীগুলি ততক্ষণ অর্ডার অভিধানের জন্য গুরুত্বপূর্ণ নয় এবং মানগুলি মিলবে। (পাইথনে অভিধানের কোনও অর্ডার নেই)

>>> {'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

নিম্নলিখিত দুটি ডিক্টের জন্য 'ডিকুইডলিস্টআইনওয়ালিউ' এবং 'রিকর্ডারডডিক্ট উইথরর্ডারডলিস্টআইনওয়ালু' যা একে অপরের কেবল পুনরায় সাজানো সংস্করণ

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

আমাকে ভুল ফল দিয়েছে, মিথ্যা।

সুতরাং আমি এইভাবে আমার নিজস্ব চটসটম অবজেক্ট কমপারেটর তৈরি করেছি:

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

যা আমাকে সঠিক প্রত্যাশিত আউটপুট দিয়েছে!

যুক্তি বেশ সহজ:

যদি অবজেক্টগুলি 'তালিকা' টাইপ করে থাকে তবে প্রথম তালিকার প্রতিটি আইটেমটি দ্বিতীয় তালিকার আইটেমগুলির সাথে না পাওয়া পর্যন্ত তুলনা করুন এবং যদি দ্বিতীয় তালিকার মধ্য দিয়ে আইটেমটি পাওয়া যায় না, তবে 'পাওয়া' = মিথ্যা হবে। 'পাওয়া' মানটি ফিরে আসে

অন্যথায় যদি তুলনামূলকভাবে অবজেক্টগুলি 'ডিক' টাইপের হয় তবে উভয় বস্তুর মধ্যে সমস্ত সম্পর্কিত কীগুলির জন্য উপস্থিত মানগুলির তুলনা করুন। (পুনরাবৃত্ত তুলনা সম্পাদন করা হয়)

অন্যথায় কেবল اعتراض 1 == اعتراض2 কল করুন। এটি ডিফল্টরূপে স্ট্রিং এবং সংখ্যার অবজেক্টের জন্য সূক্ষ্মভাবে কাজ করে এবং যারা EQ () যথাযথভাবে সংজ্ঞায়িত করা হয়।

(দ্রষ্টব্য যে অবজেক্ট 2-তে পাওয়া আইটেমগুলি সরিয়ে অ্যালগরিদম আরও উন্নত করা যেতে পারে, যাতে অবজেক্ট 1 এর পরবর্তী আইটেমটি ইতিমধ্যে 2 বস্তুর মধ্যে পাওয়া আইটেমগুলির সাথে নিজেকে তুলনা না করে)


আপনি কি দয়া করে আপনার কোডের ইন্ডেন্টেশনটি ঠিক করতে পারেন ?
কলিডিয়ার

@ কোলিডিয়ার ইন্টেন্টেশন এখন ঠিক আছে?
নিক্সভিজ

না, এখনও সেখানে সমস্যা আছে। ফাংশন শিরোনামের পরে, ব্লকটিও ইন্টেন্ট করা উচিত।
কলিডিয়ার

হ্যাঁ. আমি আবারও সম্পাদনা করেছি। আমি এটি আইডিইতে আটকানো অনুলিপি করেছি এবং এটি এখন কাজ করছে।
নিক্সভিজ

1

আপনি আপনার নিজের সমান ফাংশন লিখতে পারেন:

  • ডিক্ট সমান হলে: 1) সমস্ত কী সমান, 2) সমস্ত মান সমান
  • তালিকা সমান হলে: সমস্ত আইটেম সমান এবং একই ক্রমে in
  • আদিম সমান হলে 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 অবজেক্ট ডিবাগ করতে চান (সাধারণত, সেখানে একটি রেফারেন্স এবং একটি লক্ষ্য থাকে ), আপনি ব্যবহার করতে পারেন এমন একটি সমাধান এখানে here এটি লক্ষ্য থেকে রেফারেন্সের সাথে পৃথক / মিল নয় এমন " পথ " তালিকাভুক্ত করবে ।

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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.