Using JSON Object inside string.format()? - python

I have a Python3 script that takes an input HTML template and populates some data using this JSON and creates an output HTML.
Python Script -
with open('./jsonInp.json') as jsonFile:
jsonData = json.load(jsonFile)
inp_f = open('brandedTemplate.html', 'r+')
htmlInp = inp_f.read().format(links = jsonData['links'])
My JSON File -
{
"links" : {
"one" : "www.one.com",
"two" : "www.two.com",
}
}
Now using this in the input HTML like :
...
<a href={links.one}></a>
...
But this doesn't work. Neither does links['one'].
The JSON loading and everything works fine. I am also able to use arrays from .format function. Just can't find how to use this object anywhere. From type(jsonData['links']), I know its a Python dict.
Is there a way to use this in a html template?

Your jsonData variable is a python dict. To access values in the format mini language you need to use {my_dict[my_key]}. Note that the key is not enclosed in quotes.
To fix your example, the html input should be as follow:
...
<a href={links[one]}></a>
...

According to [Python]: Format examples, you should use (templates like):
<a href={links[one]}></a>
as html format specifier (since [Python]: json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) returns a dict).
Example:
>>> json_dict
{'links': {'one': 'www.one.com', 'two': 'www.two.com'}}
>>> href_text
'<a href={links[one]}></a>'
>>> href_text.format(links=json_dict["links"])
'<a href=www.one.com></a>'

Related

Valid (?) JSON data causing errors in Django, must be served to frontend as string and converted by JSON.parse() in javascript - why?

I have a JSON file hosted locally in my Django directory. It is fetched from that file to a view in views.py, where it is read in like so:
def Stops(request):
json_data = open(finders.find('JSON/myjson.json'))
data1 = json.load(json_data) # deserialises it
data2 = json.dumps(data1) # json formatted string
json_data.close()
return JsonResponse(data2, safe=False)
Using JsonResponse without (safe=False) returns the following error:
TypeError: In order to allow non-dict objects to be serialized set the safe parameter to False.
Similarly, using json.loads(json_data.read()) instead of json.load gives this error:
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
This is confusing to me - I have validated the JSON using an online validator. When the JSON is sent to the frontend with safe=False, the resulting object that arrives is a string, even after calling .json() on it in javascript like so:
fetch("/json").then(response => {
return response.json();
}).then(data => {
console.log("data ", data); <---- This logs a string to console
...
However going another step and calling JSON.parse() on the string converts the object to a JSON object that I can use as intended
data = JSON.parse(data);
console.log("jsonData", data); <---- This logs a JSON object to console
But this solution doesn't strike me as a complete one.
At this point I believe the most likely thing is that there is something wrong with the source JSON - (in the file character encoding?) Either that or json.dumps() is not doing what I think it should, or I am not understanding the Django API's JSONresponse function in a way I'm not aware of...
I've reached the limit of my knowledge on this subject. If you have any wisdom to impart, I would really appreciate it.
EDIT: As in the answer below by Abdul, I was reformatting the JSON into a string with the json.dumps(data1) line
Working code looks like:
def Stops(request):
json_data = open(finders.find('JSON/myjson.json'))
data = json.load(json_data) # deserialises it
json_data.close()
return JsonResponse(data, safe=False) # pass the python object here
Let's see the following lines of your code:
json_data = open(finders.find('JSON/myjson.json'))
data1 = json.load(json_data) # deserialises it
data2 = json.dumps(data1) # json formatted string
You open a file and get a file pointer in json_data, parse it's content and get a python object in data1 and then turn it back into a JSON string and store it into data2. Somewhat redundant right? Next you pass this JSON string to JsonResponse which will further try to serialize it into JSON!! Meaning you then get a string inside a string in JSON.
Try the following code instead:
def Stops(request):
json_data = open(finders.find('JSON/myjson.json'))
data = json.load(json_data) # deserialises it
json_data.close()
return JsonResponse(data, safe=False) # pass the python object here
Note: function names in python should ideally be in snake_case not PascalCase, hence instead of Stops you should use stops. See
PEP 8 -- Style Guide for Python
Code

tornado: pass string data to javascript code in a rendered page

I would like to pass dictionary data from Python into an index.html. (I'm using tornado here, but am open to other small frameworks that allow it.)
I first serialize the data with json.dumps, and this
def start_server():
data = {"a": 1, "b": 3}
string_data = json.dumps(data)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
path = os.path.join(os.path.dirname(__file__), "web")
self.render(os.path.join(path, 'index.html'), data=string_data)
return
app = tornado.web.Application([(r"/", IndexHandler)])
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
return
together with
<script>
console.log("{{data}}");
obj = JSON.parse("{{data}}");
console.log(obj);
</script>
in the index.html gives me
{"a": 1, "b": 3}
Needless to say that JSON.parse fails with those &quots.
Any hint on what's going wrong here?
You should probably try {% raw data %}. However, note that since you use the string inside a Javascript quoted string, 'data' itself should contain something suitable for that: and json.dumps() output is not valid - you'd need to escape it appropriately - either in the code that calls .render() or in the template itself (the latter is preferable). Maybe this:
obj = JSON.parse({% raw json_encode(data) %});
(using the fact that json_encode() of a string value will output "escaped-data")

Render template to escaped string for json in Django

This is more of a general python question, but it becomes a little more complicated in the context of Django.
I have a template, like this, simplified:
<span class="unit">miles</span>
I'm replacing an element with jquery and ajax:
$.getJSON('/getunit/', function(data){
$('#unitHolder').html(data.unit_html);
});
Which goes to a view function to retrieve json data (more data than just this template). So I wanted to serve it up as json, and not just a string. So, the relevant code is this:
...
context = { 'qs' : queryset }
data['unit'] = render_to_string('map/unit.html', context)
data = str(data).replace('\'','"') #json wants double quotes
return HttpResponse(data, mimetype="application/json")
This works for all of our other data, but not the template, because it has double quotes in it, that are not escaped. My question is, how do you escape a string in python to be used in a json format? Note that render_to_string() renders the string in unicode, thus u"<span>...</span>".
I've tried
import json
data['unit'] = json.dumps(render_to_string('map/unit.html', context))
But that gives me "unit": ""<span class=\\"unit\\">miles</span>"".
Also:
data['unit'] = str(render_to_string('map/unit.html', context)).replace('"','\"')
and:
data['unit'] = str(render_to_string('map/unit.html', context)).replace('"','\\"')
But neither escape the double quotes properly.
I hadn't tried json.dumps until I came into this problem. Previously I was just converting python dictionaries to strings and then replacing single quotes with double quotes. And for most of the data we've been passing to the client, it rendered correct JSON format. Now that I've tried json.dumps here in this problem, I realized that I don't need to convert dictionaries with str or replace anything. I can render the script as follows:
...
context = { 'qs' : queryset }
data['unit'] = render_to_string('map/unit.html', context)
import json # I'll import this earlier
data = json.dumps(data)
return HttpResponse(data, mimetype="application/json")
This works with all the data I'm passing to JSON format, so that works perfectly fine, and that's how I should have been doing it all along.

Python JSON encoder

I have a dict like this:
data = {"data":"http://abc/def"}
when I call json.dumps(data) I get this:
'{"data":"http://abc/def"}'
but I want this:
'{"data":"http:\/\/abc\/def"}'
because I use jquery to parse json but seems like it don't understand unescaped solidus, or is there any way to make jquery understand?
UPDATE
For example, here is my json data
{"data": ["http://abc.com/aaaaaaaa/bbbbbbbbb/cccccccccc/xyz.mp3"]}
Here is my success function
function showResult(result) {
$.each(result.data, function(i, item){
link = $('<a>').attr('href', item).text(item)
$("#result").append('<br>')
$("#result").append(link);
});
}
The result should be a hyperlink to
http://abc.com/aaaaaaaa/bbbbbbbbb/cccccccccc/xyz.mp3
But I got a hyperlink to
http://abc.com/aaaaaaaa/bbbbbbbbb/cccccccccc/xyz.mp3
If replace all '/' by '\/', everything is fine
Normally you don't escape forward slashes in JSON, but if you are certain this is your problem you can simply do this:
s = json.dumps(data)
s = s.replace("/", "\\/")

Convert JSON to XML in Python

I see a number of questions on SO asking about ways to convert XML to JSON, but I'm interested in going the other way. Is there a python library for converting JSON to XML?
Edit: Nothing came back right away, so I went ahead and wrote a script that solves this problem.
Python already allows you to convert from JSON into a native dict (using json or, in versions < 2.6, simplejson), so I wrote a library that converts native dicts into an XML string.
https://github.com/quandyfactory/dict2xml
It supports int, float, boolean, string (and unicode), array and dict data types and arbitrary nesting (yay recursion).
I'll post this as an answer once 8 hours have passed.
Nothing came back right away, so I went ahead and wrote a script that solves this problem.
Python already allows you to convert from JSON into a native dict (using json or, in versions < 2.6, simplejson), so I wrote a library that converts native dicts into an XML string.
https://github.com/quandyfactory/dict2xml
It supports int, float, boolean, string (and unicode), array and dict data types and arbitrary nesting (yay recursion).
If you don't have such a package, you can try:
def json2xml(json_obj, line_padding=""):
result_list = list()
json_obj_type = type(json_obj)
if json_obj_type is list:
for sub_elem in json_obj:
result_list.append(json2xml(sub_elem, line_padding))
return "\n".join(result_list)
if json_obj_type is dict:
for tag_name in json_obj:
sub_obj = json_obj[tag_name]
result_list.append("%s<%s>" % (line_padding, tag_name))
result_list.append(json2xml(sub_obj, "\t" + line_padding))
result_list.append("%s</%s>" % (line_padding, tag_name))
return "\n".join(result_list)
return "%s%s" % (line_padding, json_obj)
For example:
s='{"main" : {"aaa" : "10", "bbb" : [1,2,3]}}'
j = json.loads(s)
print(json2xml(j))
Result:
<main>
<aaa>
10
</aaa>
<bbb>
1
2
3
</bbb>
</main>
Load it into a dict using json.loads then use anything from this question...
Serialize Python dictionary to XML
Use dicttoxml to convert JSON directly to XML
Installation
pip install dicttoxml
or
easy_install dicttoxml
In [2]: from json import loads
In [3]: from dicttoxml import dicttoxml
In [4]: json_obj = '{"main" : {"aaa" : "10", "bbb" : [1,2,3]}}'
In [5]: xml = dicttoxml(loads(json_obj))
In [6]: print(xml)
<?xml version="1.0" encoding="UTF-8" ?><root><main type="dict"><aaa type="str">10</aaa><bbb type="list"><item type="int">1</item><item type="int">2</item><item type="int">3</item></bbb></main></root>
In [7]: xml = dicttoxml(loads(json_obj), attr_type=False)
In [8]: print(xml)
<?xml version="1.0" encoding="UTF-8" ?><root><main><aaa>10</aaa><bbb><item>1</item><item>2</item><item>3</item></bbb></main></root>
For more information on dicttoxml
I found xmltodict to be useful. Looks like it was released after some of the posts here. https://pypi.org/project/xmltodict/
import xmltodict
import json
sample_json = {"note": {"to": "Tove", "from": "Jani", "heading": "Reminder", "body": "Don't forget me this weekend!"}}
#############
#json to xml
#############
json_to_xml = xmltodict.unparse(sample_json)
print(json_to_xml)
#############
#xmlto json
#############
x_to_j_dict = xmltodict.parse(json_to_xml)
x_to_j_string = json.dumps(x_to_j_dict)
back_to_json = json.loads(x_to_j_string)
print(back_to_json)
from json import loads
from dicttoxml import dicttoxml
s='{"main" : {"aaa" : "10", "bbb" : [1,2,3]}}'
xml = dicttoxml(loads(s))
Or if your data is stored in a pandas data.frame as mine often is:
df['xml'] = df['json'].apply(lambda s: dicttoxml(json.loads(s))

Categories

Resources