json.dumps vs flask.jsonify - python

I am not sure I understand the purpose of the flask.jsonify method. I try to make a JSON string from this:
data = {"id": str(album.id), "title": album.title}
but what I get with json.dumps differs from what I get with flask.jsonify.
json.dumps(data): [{"id": "4ea856fd6506ae0db42702dd", "title": "Business"}]
flask.jsonify(data): {"id":…, "title":…}
Obviously I need to get a result that looks more like what json.dumps returns. What am I doing wrong?

The jsonify() function in flask returns a flask.Response() object that already has the appropriate content-type header 'application/json' for use with json responses. Whereas, the json.dumps() method will just return an encoded string, which would require manually adding the MIME type header.
See more about the jsonify() function here for full reference.
Edit:
Also, I've noticed that jsonify() handles kwargs or dictionaries, while json.dumps() additionally supports lists and others.

You can do:
flask.jsonify(**data)
or
flask.jsonify(id=str(album.id), title=album.title)

This is flask.jsonify()
def jsonify(*args, **kwargs):
if __debug__:
_assert_have_json()
return current_app.response_class(json.dumps(dict(*args, **kwargs),
indent=None if request.is_xhr else 2), mimetype='application/json')
The json module used is either simplejson or json in that order. current_app is a reference to the Flask() object i.e. your application. response_class() is a reference to the Response() class.

The choice of one or another depends on what you intend to do.
From what I do understand:
jsonify would be useful when you are building an API someone would query and expect json in return. E.g: The REST github API could use this method to answer your request.
dumps, is more about formating data/python object into json and work on it inside your application. For instance, I need to pass an object to my representation layer where some javascript will display graph. You'll feed javascript with the Json generated by dumps.

consider
data={'fld':'hello'}
now
jsonify(data)
will yield {'fld':'hello'} and
json.dumps(data)
gives
"<html><body><p>{'fld':'hello'}</p></body></html>"

Related

FASTAPI: what is`(..)` in the Body(...) while reading from a post request?

I am trying to read body from my POST request using FastAPI.
However i am not able to understand what (...) argument for the Body function
Here is my code :
#app.post('/createPosts')
def create_post(payload: dict = Body(...)):
print(payload)
return {'message': 'succesfully created post'}
... (Ellipsis) was the way of declaring a required parameter in FastAPI.
However, from 0.78.0, you can just omit the default value to do that.
See release note and documentation for details.
Probably duplicated question. Here is another one:
What does the Ellipsis object do?
It is part of Python, not FastAPI

Class that returns json, python

I have a python class that should return a json, it looks something like this:
class ScanThis():
def__init__(self, test):
data={}
if test>5:
data["amount"] = test
self.json_data = json.dumps(data)
else:
data["fail"] = test
self.json_data = json.dumps(data)
def __str__(self):
return self.json_data
and I'm trying to call it like so:
output= json.loads(ScanThis(8))
print(output["command"])
But I get this error:
TypeError: the JSON object must be str, bytes or bytearray, not 'ScanThis'
I believe my earlier clas returns an object of type ScanThis() rather than a JSon like I wanted. I just wanted to now how I'd fix this
Thank you
PS: I apologise if this code is rough or invalid, it's not the actual code, just something similar I made up
Update: Again, this isn't the real code, it's just a small basic fragment of the actual code. There's a good reason I'm using a class, and a json is used cause data transfer over the internet is involved
Use str(..)
You can't call json.loads on a ScanThis object directly. So that won't work. Like the error says, json.loads expects a str, bytes or bytearray object.
You can however use str(..) to invoke the __str__(self) method, and thus obtain the JSON data:
output = json.loads(str(ScanThis(8)))
# ^ get the __str__ result
Use another method
That being said, it is usually a better idea to define a method, for instance to_json to obtain the JSON data, since now you have made str(..) return a JSON object. So perhaps a more elegant way to do this is the following:
class ScanThis():
def__init__(self, test):
data={}
if test>5:
data["amount"] = test
self.json_data = json.dumps(data)
else:
data["fail"] = test
self.json_data = json.dumps(data)
def to_json(self):
return self.json_data
and call it with:
output = json.loads(ScanThis(8).to_json())
Now you can still use __str__ for another purpose. Furthermore by using to_json you make it explicit that the result will be a JSON string. Using str for JSON conversion is of course not forbidden, but str(..) as a name, does not provide much guarantees about the format of the result whereas to_json (or another similar name) strongly hints that you will obtain JSON data.
I don't think you are wanting to use a class there at all.
Instead, try using a function that returns a string. For example:
def scan_this(test):
data={}
if test>5:
data["amount"] = test
json_data = json.dumps(data)
else:
data["fail"] = test
json_data = json.dumps(data)
return json_data
output = json.loads(scan_this(8))
However!! Now you are just doing extra work for nothing? Why do you need to serialize a python dictionary as a json formatted string, and then load it back into a python dictionary straight away? While you are working with data in python, it's best to keep it as native data types, and only using the json module to either load from a string/file you already have, or serialize to a string/file for storage or transfer (eg sending over the internet).

Why don't web2py json services treat lists properly?

The following works for json whose outermost container is an object like { ... }
#service.json
def index():
data = request.vars
#fields are now accessible via data["fieldname"] or data.fieldname
#processing must be done to turn Storage object into dict()
return data_as_dict
If you post a list however, it does not work
POST:
[
{"test": 1}
]
data will be an empty Storage object and data[0] will be None
The workaround is simple:
#service.json #so output is still returned as json
def index():
data = json.loads(request.body.read())
return data
data is now a dict in cases of object style JSON (easier to work with than a Storage object imo) and a native list when the JSON is a list.
My question is why is this not the default behaviour? Why should a JSON service not accept valid JSON?
The #service.json decorator simply registers a function so it can be accessed via a controller that returns a Service object. The decorator ensures that the service controller returns a JSON response when the decorated function is called, but it does nothing regarding the processing of JSON input.
In any case, your problem is not with the #service.json decorator but with a misunderstanding regarding request.vars. request.vars is a dictionary-like object that is populated with keys and values from the query string and/or the request body (if the request body includes form variables or a JSON object of keys and values). It is not intended to simply be a copy of any arbitrary data structure that is posted in the request body. So, if you post a JSON array in the request body, it would not make sense to copy that array to request.vars, as it is not the appropriate type of data structure. If you want to post a JSON array, the correct way to process it is to read the request body, as you have done.
Also, note that because your index function does not take any arguments and therefore does not take advantage of the #service decorator's ability to map parameters from the HTTP request into function arguments, you could simplify your code by foregoing the #service decorator and accessing the index function more directly:
def index():
data = json.loads(request.body.read())
return data
Assuming index is in the default.py controller, you could post JSON to /yourapp/default/index.json (note the .json extension), and you will get back a JSON response.

tornado maps GET and POST arguments to lists. How can I disable this "feature"?

The HTTPRequest class in the tornado* web framework helpfully maps GET and POST arguments to lists. I understand why -- in case a given argument name is used multiple times. But for some RequestHandlers, this is a pain. For instance, if I want to pass a json object and parse it as-is on the server.
What's the most straightforward way to disable the map-to-list behavior so that I can send unaltered json to a tornado/cyclone server?
*Cyclone, actually, in case there's an implementation difference here.
Instead of accessing self.request.arguments directly you should use the accessor functions:
self.get_argument("ID", default=None, strip=False)
This returns a single item.
If you want to turn the arguments into a JSON object you can quite easily do so:
json.dumps({ k: self.get_argument(k) for k in self.request.arguments })
I'm going to go with "you're out of luck." You could re-write the class in question (looks like that would not be fun), but aside from that I don't see many options.
I would just use a dict comprehension.
{k:''.join(v) for k,v in self.request.arguments.iteritems()}

Standardised JSON response from views

When my page POSTs a form to my Django view, the view returns a response with some data but soon I ran into the issue that my views returned data in different formats and different sets of information. I've thought of using JSON as a standard format to return my data.
There are two types of statuses, success and failure. When the POST was successful, it just returns success but when it has failed, it returns a sub group called errors which contains a of fields and that field's error.
Here's a sample failure response:
{"failure": {
"errors":[
{"fieldname": "<fieldname>",
"fielderror": "<fielderror>"
},
{"fieldname": "<fieldname>",
"fielderror": "<fielderror>"
}]
}}
Here's a sample success response:
{"success": {
"data":[
{"fieldname": "<fieldname>",
"fielddata": "<fielddata>"
},
{"fieldname": "<fieldname>",
"fielddata": "<fielddata>"
}]
}}
(The success response has data fields because quite often you like to return some data i.e. key of newly created DB record.)
This is what I've come up with but so many people using Django out there, I'm wondering whether there is a standard way/more robust of doing this or some module to help with this.
Thanks.
been there. I wrote the solution my own (since it's simple. I dont know if theres a module out there for this). This is just a json response wrapper
from django.utils import simplejson
from django.http import HttpResponse
class JsonResponse(HttpResponse):
def __init__(self, data):
content = simplejson.dumps(data)
super(JsonResponse, self).__init__(content=content,
mimetype='application/json')
class Success(JsonResponse):
def __init__(self, something):
x = something # set your data here
content = {'success': {'data': x}}
super(Success, self).__init__(content)
class Failure(JsonResponse):
def __init__(self, something):
x = something # set your data
content = {'failures': {'errors': x}}
super(Failure, self).__init__(content)
something like that. In my case, I make Success and Failure accept a string and a dictionary for arguments (like Success(data)) to make it even easier
If your structure is quite complex (or youre too lazy too write them manually), write your own wrapper for that complicated structure (so you can create the response with even less typing), and make the Success/Failure accepts that as argument.
Do you know how to use Python dicts? If so then there are libraries which convert your Python dict into valid JSON. Django's built-in is for example is simplejson. Here an example:
from django.utils import simplejson
def my_view(request):
....
my_json = simplejson.dumps(my_dict) #dumps a JSON string from a dict
my_dict_again = simplejson.loads(my_json) #loads a Python dict from a JSON string
return HttpResponse(json, mimetype="application/json")
Well, I don't know how much this will help you out, but Python does have a module json.
>>> import json
>>> json.dumps({'a':'b', 'c':'d'})
'{"a": "b", "c": "d"}'
I think the front end is going to make a stronger case for how you should format your data then some "standard", Different client side frameworks expect different things, and adapt to differences from that expectations better or worse than that.
One way I try to make my own life easier is to have the 'shape' of the response always roughly the same, for example, the response will always have a 'status' property, which always has an 'errors' property, and if there were no errors, then the value of result.status.errors is null. If there were errors, it'd be a list of strings explaining what went wrong. even if there were errors, there'd be a 'value' property, which would be either null if the request couldn't be serviced, or the requested resource if it could.

Categories

Resources