1

I am writing a api to download json file. Suppose I have 3 dict:

{"task_name":"task1","task_info":"aaaaa"}
{"task_name":"task2","task_info":"bbbbb"}
{"task_name":"task3","task_info":"ccccc"}

I want to return these dict into one json file.Here is what I did:

data = json.dumps(tasks_info, default=str)
response = make_response(data, 200, {'mimetype': 'application/json'})
response.headers['Content-Disposition'] = "attachment;filename={}.json".format(urllib.parse.quote('result'))
return response

tasks_info is a list that contains 3 dict. The result file open like that:

[{"task_name":"task1","task_info":"aaaaa"},{"task_name":"task2","task_info":"bbbbb"},{"task_name":"task3","task_info":"ccccc"}]

It is a very long line. What I want to get looks like

[   // the '[' and ']' is not necessary
{"task_name":"task1","task_info":"aaaaa"},
{"task_name":"task2","task_info":"bbbbb"},
{"task_name":"task3","task_info":"ccccc"}
]

I want every dict show in a distinct line instead of every dict shown in same line. Are there any way to change the result file look like?

McCree Feng
  • 179
  • 4
  • 12

1 Answers1

0

The relevant command that formats this output takes place in json.dumps(). There are a bunch of commands you can give to format your json. A conventional approach is to define the indent level:

import json

tasks_info = [{"task_name":"task1","task_info":"aaaaa"},{"task_name":"task2","task_info":"bbbbb"},{"task_name":"task3","task_info":"ccccc"}]
data = json.dumps(tasks_info, indent=2)
print(data)

Which pretty-prints the json into:

[
  {
    "task_info": "aaaaa", 
    "task_name": "task1"
  }, 
  {
    "task_info": "bbbbb", 
    "task_name": "task2"
  }, 
  {
    "task_info": "ccccc", 
    "task_name": "task3"
  }
]

To achieve the exact formatting you desire, you need to define your own formatter class. I used this answer and edited it to fit your formatting requirements. Here is the full code:

import _ctypes
import json
import re

class OneDictPerLine(object):
    def __init__(self, value):
        self.value = value
    def __repr__(self):
        if not isinstance(self.value, list):
            return repr(self.value)
        else:  # Sort the representation of any dicts in the list.
            reps = ('{{{}}}'.format(', '.join(
                        ('{!r}: {!r}'.format(k, v) for k, v in sorted(v.items()))
                    )) if isinstance(v, dict)
                        else
                    repr(v) for v in self.value)
            return '[ \n' + ',\n'.join(reps) + '\n ]'


def di(obj_id):
    """ Reverse of id() function. """
    # from https://stackoverflow.com/a/15012814/355230
    return _ctypes.PyObj_FromPtr(obj_id)


class MyEncoder(json.JSONEncoder):
    FORMAT_SPEC = "@@{}@@"
    regex = re.compile(FORMAT_SPEC.format(r"(\d+)"))

    def default(self, obj):
        return (self.FORMAT_SPEC.format(id(obj)) if isinstance(obj, OneDictPerLine)
                else super(MyEncoder, self).default(obj))

    def encode(self, obj):
        format_spec = self.FORMAT_SPEC  # Local var to expedite access.
        json_repr = super(MyEncoder, self).encode(obj)  # Default JSON repr.

        # Replace any marked-up object ids in the JSON repr with the value
        # returned from the repr() of the corresponding Python object.
        for match in self.regex.finditer(json_repr):
            id = int(match.group(1))
            # Replace marked-up id with actual Python object repr().
            json_repr = json_repr.replace(
                       '"{}"'.format(format_spec.format(id)), repr(di(id)))

        return json_repr


tasks_info = [{"task_name":"task1","task_info":"aaaaa"},{"task_name":"task2","task_info":"bbbbb"},{"task_name":"task3","task_info":"ccccc"}]
data = json.dumps(OneDictPerLine(tasks_info), cls=MyEncoder)
print(data)
#make response and return data ...

output:

[ 
{'task_info': 'aaaaa', 'task_name': 'task1'},
{'task_info': 'bbbbb', 'task_name': 'task2'},
{'task_info': 'ccccc', 'task_name': 'task3'}
]
pastaleg
  • 1,782
  • 2
  • 17
  • 23
  • that's cool. Are there any way if my output looks like ``` {'task_info': 'aaaaa', 'task_name': 'task1'}, {'task_info': 'bbbbb', 'task_name': 'task2'}, {'task_info': 'ccccc', 'task_name': 'task3'} ``` I mean drop the array, every single line is a dict – McCree Feng May 09 '20 at 09:53
  • @McCreeFeng Certainly. Replace the return statement of `OneDictPerLine()` with `return ',\n'.join(reps)` – pastaleg May 09 '20 at 11:05