1

I have a JSON file like this:

{
    "top_key1": {
                "bottom.key1": "one",
                "bottom.key2": "two"
                },
    "top_key2": [
                "bottom.key1": "one",
                "bottom.key2": "two",
                ]
}

And I need to store in a data structure that won't allow me to store a key with a period (.) on it. How can I traverse this JSON structure so I replace every . occurrence by _? The final result would be:

{
    "top_key1": {
                "bottom_key1": "one",
                "bottom_key2": "two"
                },
    "top_key2": [
                "bottom_key1": "one",
                "bottom_key2": "two",
                ]
}

The JSON file can be nested several (unknown) times and there can be . on values also, but I don't want them replaced by _. Also, value of "top_key2" is a list, which should be preserved.

licorna
  • 5,670
  • 6
  • 28
  • 39

1 Answers1

3

Not too hard I think, just use isinstance to check if the current value is another dict:

nested_dict = {
    "top_key1": {
                "bottom.key1": "one",
                "bottom.key2": "two"
                },
    "top_key2": {
                "bottom.key1": "one",
                "bottom.key2": "two",
                }
}

def replace_dots(nested_dict):
    result_dict = {}
    for key, val in nested_dict.items():
        key = key.replace('.', '_')
        if isinstance(val, dict):
            result_dict[key] = replace_dots(val)
        else:
            result_dict[key] = val
    return result_dict

fixed = replace_dots(nested_dict)

fixed
Out[4]: 
{'top_key1': {'bottom_key1': 'one', 'bottom_key2': 'two'},
 'top_key2': {'bottom_key1': 'one', 'bottom_key2': 'two'}}

I'm not sure if I fully understand your edit, as your example list still has a key-value structure, but adding an extra case to deal with lists is pretty simple:

nested_dict2 = {
    "top_key1": {
                "bottom.key1": "one",
                "bottom.key2": "two"
                },
    "top_key2": ["list.item1", "list.item2"]
}

def replace_dots(nested_dict):
    result_dict = {}
    for key, val in nested_dict.items():
        key = key.replace('.', '_')
        if isinstance(val, dict):
            result_dict[key] = replace_dots(val)
        elif isinstance(val, list):
            cleaned_val = [v.replace('.', '_') for v in val]
            result_dict[key] = cleaned_val
        else:
            result_dict[key] = val
    return result_dict

replace_dots(nested_dict2)
Out[7]: 
{'top_key1': {'bottom_key1': 'one', 'bottom_key2': 'two'},
 'top_key2': ['list_item1', 'list_item2']}
Marius
  • 58,213
  • 16
  • 107
  • 105
  • 1
    Thanks Marius. The JSON object also has lists on it, and keys with periods on it, so this method is missing that particular use case. – licorna Oct 22 '14 at 22:22
  • @licorna: see my edit, you can add an extra type-check to deal with the lists, although the example list in your question didn't entirely make sense to me as JSON so I took a bit of a guess at what you meant. – Marius Oct 23 '14 at 00:12