1

I'm trying to compare two complex json strings using python but I'm having some issues. The two strings I have are the same, but some fields of child elements appear in different orders.

EG:

a = """
{
   "sgroupname":"windows_securezone",
   "ipperms":[
      {
         "IpProtocol":"-1",
         "IpRanges":[
            {
               "CidrIp":"194.66.78.xx/32"
            },
            {
               "CidrIp":"86.17.73.xx/32"
            }
         ],
         "UserIdGroupPairs":[

         ],
         "PrefixListIds":[

         ]
      }
   ]
}
"""
b = """
{
   "sgroupname":"windows_securezone",
   "ipperms":[
      {
         "IpProtocol":"-1",
         "IpRanges":[
            {
               "CidrIp":"86.17.73.xx/32"
            },
            {
               "CidrIp":"194.66.78.xx/32"
            }
         ],
         "UserIdGroupPairs":[

         ],
         "PrefixListIds":[

         ]
      }
   ]
}
"""

The fact that the IpRanges list under the ipperms element returns in a different order seems to break the comparison; if I switch them to the same order the comparison works correctly.

I've tried using OrderedDict, the json.dumps 'sort_keys=True' parameter, and using the sorted() function, but I can't get the strings to compare. Any ideas?

Any help would be greatly appreciated!

Ben
  • 13
  • 3
  • 1
    `sort_keys` isn't going to help you because `IpRanges` is a *list*. It already has an ordering. If you wanted to compare these, you would need to sort any lists contained in your objects first...which is going to be tricky, since the members of those lists are dicts, which are not inherently sortable. – larsks Apr 07 '16 at 16:29
  • Is there some reason you can't parse them both back to Python native types with `json.loads` and just do plain equality tests? Also, what larsks said. :-) – ShadowRanger Apr 07 '16 at 16:34
  • @ShadowRanger The issue is the data will not necessarily always be in this format; sometimes there will be lists present, other times not, so I'm unsure how I could implement that solution... – Ben Apr 07 '16 at 16:38
  • You can't without first writing some code to normalize the data present in your two dictionaries. – larsks Apr 07 '16 at 16:42
  • Bugger. I guess in that case I'll have to sort the data before it is stored and hopefully then the comparison should be better. Thanks! – Ben Apr 07 '16 at 17:02

1 Answers1

1

try this approach:

In [200]: a
Out[200]:
{'ipperms': [{'IpProtocol': '-1',
   'IpRanges': [{'CidrIp': '194.66.78.xx/32'}, {'CidrIp': '86.17.73.xx/32'}],
   'PrefixListIds': [],
   'UserIdGroupPairs': []}],
 'sgroupname': 'windows_securezone'}

In [201]: b
Out[201]:
{'ipperms': [{'IpProtocol': '-1',
   'IpRanges': [{'CidrIp': '86.17.73.xx/32'}, {'CidrIp': '194.66.78.xx/32'}],
   'PrefixListIds': [],
   'UserIdGroupPairs': []}],
 'sgroupname': 'windows_securezone'}

In [202]: 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
   .....:

In [203]: ordered(a) == ordered(b)
Out[203]: True

In [204]: a == b
Out[204]: False
Community
  • 1
  • 1
MaxU - stand with Ukraine
  • 205,989
  • 36
  • 386
  • 419
  • If you try that with the original data, you'll see that frozenset is actually discarding information. All that tells you is whether a and b have the same keys (i.e., you are essentially compareing a.keys() to b.keys()). – larsks Apr 07 '16 at 16:34
  • @larsks, tried it with the original data - it's still working. what am i doing wrong? – MaxU - stand with Ukraine Apr 07 '16 at 16:38
  • Look at the return value of, e.g., `frozenset(a)`. That only considers the top-level key names from the original dictionary, and does not thing to compare the values. – larsks Apr 07 '16 at 16:40