3

Is there a way to add duplicate keys to json with python?

From my understanding, you can't have duplicate keys in python dictionaries. Usually, how I go about creating json is to create the dictionary and then json.dumps. However, I need duplicated keys within the JSON for testing purposes. But I can't do so because I can't add duplicate keys in a python dictionary. I am trying to doing this in python 3

6
  • @DonkeyKong unfortunately not... Commented Apr 8, 2015 at 16:16
  • Cannot have duplicate keys in dictionary. Cannot have duplicate keys in json. Commented Apr 8, 2015 at 16:22
  • Can have duplicate keys in JSON the fileformat. Commented Apr 8, 2015 at 16:23
  • Duplicate keys in JSON is not interoperable, as stated in the RFC: An object whose names are all unique is interoperable. However, you can write your own JSON encoder/decoder in Python such that a dict with multiple values in a list is collapsed into multiple k:v pairs Commented Apr 8, 2015 at 16:56
  • @taesu you can have duplicate keys in JSON Commented Apr 8, 2015 at 19:19

3 Answers 3

7

You could always construct such a string value by hand.

On the other hand, one can make the CPython json module to encode duplicate keys. This is very tricky in Python 2 because json module does not respect duck-typing at all.

The straightforward solution would be to inherit from collections.Mapping - well you can't, since "MyMapping is not a JSON serializable."

Next one tries to subclass a dict - well, but if json.dumps notices that the type is dict, it skips from calling __len__, and sees the underlying dict directly - if it is empty, {} is output directly, so clearly if we fake the methods, the underlying dictionary must not be empty.

The next source of joy is that actually __iter__ is called, which iterates keys; and for each key, the __getitem__ is called, so we need to remember what is the corresponding value to return for the given key... thus we arrive to a very ugly solution for Python 2:

class FakeDict(dict):
    def __init__(self, items):
        # need to have something in the dictionary
        self['something'] = 'something'
        self._items = items

    def __getitem__(self, key):
        return self.last_val

    def __iter__(self):
        def generator():
            for key, value in self._items:
                self.last_val = value
                yield key

        return generator()

In CPython 3.3+ it is slightly easier... no, collections.abc.Mapping does not work, yes, you need to subclass a dict, yes, you need to fake that your dictionary has content... but the internal JSON encoder calls items instead of __iter__ and __getitem__!

Thus on Python 3:

import json

class FakeDict(dict):
    def __init__(self, items):
        self['something'] = 'something'
        self._items = items
    def items(self):
        return self._items

print(json.dumps(FakeDict([('a', 1), ('a', 2)])))

prints out

{"a": 1, "a": 2}
Sign up to request clarification or add additional context in comments.

5 Comments

Hmm running this code in my interpreter and I am getting '{}'
Hem, forgot to add the faked contents to Python 3 version
its not working .it just printing {'something':'something'}
@VennilaM what, where?
Works like a charm. It can be used as simply as obj = json.loads(data, object_pairs_hook=FakeDict) and then json.dumps(obj).
2

Thanks a lot Antti Haapala, I figured out you can even use this to convert an array of tuples into a FakeDict:

def function():
    array_of_tuples = []
    array_of_tuples.append(("key","value1"))
    array_of_tuples.append(("key","value2"))
    return FakeDict(array_of_tuples)

print(json.dumps(function()))

Output:

{"key": "value1", "key": "value2"}

And if you change the FakeDict class to this Empty dictionaries will be correctly parsed:

class FakeDict(dict):
    def __init__(self, items):
        if items != []:
            self['something'] = 'something'
        self._items = items
    def items(self):
        return self._items
def test():
    array_of_tuples = []
    return FakeDict(array_of_tuples)

print(json.dumps(test()))

Output:

"{}"

Comments

0

Actually, it's very easy:

$> python -c "import json; print json.dumps({1: 'a', '1': 'b'})"
{"1": "b", "1": "a"}

1 Comment

This will only work directly in very limited circumstances. The underlying idea is to have multiple distinct objects with the same string representation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.