[Posted by George V. Reilly]
We're writing our new web services in Python (a story for another day). I thought I'd write up a few hard-won tips on using the Cheetah Template library.
Among other things, I'm using Cheetah to generate JSON. I wanted strict control over the layout of the result, including the order of dictionary members. Many of the properties that I'm serializing need custom handling; for example, datetimes require a non-standard format string. It might have been possible to use simplejson, but I didn't think that I could get the control that I wanted.
An explicit transformation via a template is much easier to control than an implicit one, such as simplejson's. The main downside is that if additional properties are added to the underlying data structures, I need to remember to update the template. If I were using implicit serialization à la simplejson, it would just work.
One of my requirements was that lists (arrays) and dictionaries should be prettyprinted:
[
{
"foo": "bar"
},
{
"foo": "baz"
},
{
"foo": "quux"
}
]Note that all but the last } is followed by a , separator—trailing commas in JavaScript initializers cause script errors in Internet Explorer, and are disallowed by the JSON spec. Getting both the comma and the indentation right proved tricky.
1 |
""" |
The #slurp directive discards everything to the end of the line, including the newline. Line 5, $sep#slurp, produces no output on the first iteration; it must be left-aligned. Line 8, }#slurp, produces a properly indented }, but no newline. On subsequent iterations, line 5 generates a comma and a newline, immediately after the } from the previous iteration. Line 11, the blank line between #end for and the closing ], ensures that the ] ends up on a new line; without it, the }#slurp would cause the last line to be }].
Here's a more complex example.
It's not safe, of course, to send arbitrary strings without encoding them as JSON. One additional requirement was that some strings also needed to be HTML escaped. Cheetah has no built-in function for JSON encoding, but it's easy to import a Python function. Here, I've defined a Cheetah function, $jh(), in terms of json_html() from the surrounding Python module, cheetah_demo.py.
#!/usr/bin/env python
import Cheetah.Template
from simplejson import JSONEncoder
import cgi
def json(s):
return JSONEncoder().encode(s)
def json_html(s):
"""
Convert a string to JSON notation with &, <, and > replaced by HTML entities.
"""
return cgi.escape( json(s) )
attendees_json_template_def="""#encoding UTF-8
{
#import cheetah_demo
#def jh($t): $cheetah_demo.json_html($t)
#if len($attendees) == 0
"Attendees": []
#else
"Attendees": [
#set sep = ''
#for $attendee in $attendees
$sep#slurp
{
"Name": $jh($attendee.display_name),
"IColor": "$attendee.color"
}#slurp
#set sep = ',\\n'
#end for
]
#end if
}"""
def cheetah_attendees():
attendees = [
{"display_name": "Fred Flintstone", "color": "#ffabcd"},
{"display_name": "Barney Rubble", "color": "#123456"},
{"display_name": "Caspar the Ghost", "color": "#eee"},
]
t = Cheetah.Template.Template(attendees_json_template_def, searchList=[{
'attendees': attendees
}])
print t
if __name__ == '__main__':
cheetah_attendees()One other tip: I found the hard way that despite its superficial similarities to the C preprocessor, Cheetah is picky about spaces after #: the innocuous # for yields a cryptic error that was tedious to track down.





Comments