Transform first level JSON Key with Python

Question:

I have the following JSON:

{
    "1605855600000":
    [
        {
            "id": "1",
            "value": "1. Choice 1",
            "checked": true
        },
        {
            "id": "2",
            "value": "2. Choice 2",
            "checked": false
        },
        {
            "id": "3",
            "value": "3. Choice 3",
            "checked": false
        },
        {
            "id": "4",
            "value": "4. Choice 4",
            "checked": false
        },
        {
            "id": "5",
            "value": "5. Choice 5",
            "checked": false
        }
    ],
    "1604732400000":
    [
        {
            "id": "1",
            "value": "1. Choice 1",
            "checked": false
        },
        {
            "id": "2",
            "value": "2. Choice 2",
            "checked": false
        },
        {
            "id": "3",
            "value": "3. Choice 3",
            "checked": false
        },
        {
            "id": "4",
            "value": "4. Choice 4",
            "checked": false
        },
        {
            "id": "5",
            "value": "5. Choice 5",
            "checked": false
        }
    ]
}

I’m not sure the exact terminology but the "keys" like ‘1605855600000’ and ‘1604732400000’ are UNIX timestamps that I need to convert in Python.

Here is what I have so far:

def convert_timestamp(obj):
    for key in obj.keys():
        timestamp = int(key) / 1000
        dt_object = datetime.fromtimestamp(timestamp)
        date = dt_object.strftime("%B %d, %Y")
        new_key = date
        if new_key != key:
            obj[new_key] = obj[key]
            del obj[key]
    return obj

data = json.loads(data, object_hook=convert_timestamp)

However, the error I receive says:
ValueError: invalid literal for int() with base 10: 'id'
which means that it is accessing a level below what I am trying to change.

I know the logic inside the definition works to output the string that I need as the final result, but accessing and replacing those with the current logic isn’t working.

The final output needs to look something like:

{
    "12 November, 2020":
    [
        {
            "id": "1",
            "value": "1. Choice 1",
            "checked": true
        },
...
Asked By: Hayden

||

Answers:

Did you mean:

data = json.loads(data)

data = convert_timestamp(data)
Answered By: quamrana

You misused object_hook parameter in json.loads.

From the docs

object_hook is an optional function that will be called with the
result of any object literal decoded (a dict). The return value of
object_hook will be used instead of the dict. This feature can be used
to implement custom decoders (e.g. JSON-RPC class hinting).

Therefore, it takes the first object found by the key. As @quamrana mentioned in his answer you need to do:

json_data = json.loads(data)
converted_data = convert_timestamp(json_data)

Also, it is considered a good practice not to override data but use separate variables.

Answered By: Dmytro Chasovskyi

You are much better off making a new dict than messing with the keys:

import json
from datetime import datetime

data_str = '''{
    "1605855600000":
    [
        { "id": "1", "value": "1. Choice 1", "checked": true },
        { "id": "2", "value": "2. Choice 2", "checked": false },
        { "id": "3", "value": "3. Choice 3", "checked": false },
        { "id": "4", "value": "4. Choice 4", "checked": false },
        { "id": "5", "value": "5. Choice 5", "checked": false }
    ],
    "1604732400000":
    [
        { "id": "1", "value": "1. Choice 1", "checked": false },
        { "id": "2", "value": "2. Choice 2", "checked": false },
        { "id": "3", "value": "3. Choice 3", "checked": false },
        { "id": "4", "value": "4. Choice 4", "checked": false },
        { "id": "5", "value": "5. Choice 5", "checked": false }
    ]
}'''

def convert_timestamp(key):
    timestamp = int(key) / 1000
    dt_object = datetime.fromtimestamp(timestamp)
    return dt_object.strftime("%B %d, %Y")

def convert_keys(obj):
    return {convert_timestamp(key):val for (key,val) in obj.items()}

data = json.loads(data_str)

new_data = convert_keys(data)
print(new_data)

Output:

{'November 20, 2020': [{'id': '1', 'value': '1. Choice 1', 'checked': True}, {'id': '2', 'value': '2. Choice 2', 'checked': False}, {'id': '3', 'value': '3. Choice 3', 'checked': False}, {'id': '4', 'value': '4. Choice 4', 'checked': False}, {'id': '5', 'value': '5. Choice 5', 'checked': False}], 'November 07, 2020': [{'id': '1', 'value': '1. Choice 1', 'checked': False}, {'id': '2', 'value': '2. Choice 2', 'checked': False}, {'id': '3', 'value': '3. Choice 3', 'checked': False}, {'id': '4', 'value': '4. Choice 4', 'checked': False}, {'id': '5', 'value': '5. Choice 5', 'checked': False}]}
Answered By: quamrana

Here is how I eventually solved it:

json_data = json.loads(data)
dict_keys = json_data.keys()

timestamp = []
for x in dict_keys:
    date_val = int(x) / 1000
    dt_object = datetime.fromtimestamp(date_val)
    date = dt_object.strftime("%B %d, %Y")
    timestamp.append(date)

final_dict = dict(zip(timestamp, list(json_data.values())))

Essentially it ended up being easier to pull the keys into a list, perform the transformation, and then merge that list back to the values as the new keys instead of trying to do it in one fell swoop. For someone who has strict performance requirements this might not be ideal but for this use case it will work.


This answer was posted as an edit to the question Transform first level JSON Key with Python – Solved by the OP Hayden under CC BY-SA 4.0.

Answered By: vvvvv
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.