Liondancer
Liondancer

Reputation: 16469

Adding duplicate keys to JSON with python

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

Upvotes: 3

Views: 4798

Answers (3)

Nice Zombies
Nice Zombies

Reputation: 1097

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:

"{}"

Upvotes: 2

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}

Upvotes: 7

lbolla
lbolla

Reputation: 5411

Actually, it's very easy:

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

Upvotes: 0

Related Questions