Home | History | Annotate | Download | only in shared
      1 """
      2 This module began as a more-or-less direct translation of jsonview.js from the
      3 JSONView project, http://code.google.com/p/jsonview.  Here's the original
      4 JSONView license:
      5 
      6 ---
      7 MIT License
      8 
      9 Copyright (c) 2009 Benjamin Hollis
     10 
     11 Permission is hereby granted, free of charge, to any person obtaining a copy
     12 of this software and associated documentation files (the "Software"), to deal
     13 in the Software without restriction, including without limitation the rights
     14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     15 copies of the Software, and to permit persons to whom the Software is
     16 furnished to do so, subject to the following conditions:
     17 
     18 The above copyright notice and this permission notice shall be included in
     19 all copies or substantial portions of the Software.
     20 
     21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     27 THE SOFTWARE.
     28 ---
     29 """
     30 
     31 import re
     32 import json
     33 
     34 _HTML_DOCUMENT_TEMPLATE = """
     35 <!DOCTYPE html>
     36 <html>
     37 <head>
     38 <title>JSON output</title>
     39 <link rel="stylesheet" type="text/css" href="/afe/server/static/jsonview.css">
     40 </head>
     41 <body>
     42 <div id="json">
     43 %s
     44 </div>
     45 </body>
     46 </html>
     47 """
     48 
     49 class JsonHtmlFormatter(object):
     50     def _html_encode(self, value):
     51         if value is None:
     52             return ''
     53         return (str(value).replace('&', '&amp;').replace('"', '&quot;')
     54                 .replace('<', '&lt;').replace('>', '&gt;'))
     55 
     56 
     57     def _decorate_with_span(self, value, className):
     58         return '<span class="%s">%s</span>' % (
     59                 className, self._html_encode(value))
     60 
     61 
     62     # Convert a basic JSON datatype (number, string, boolean, null, object,
     63     # array) into an HTML fragment.
     64     def _value_to_html(self, value):
     65         if value is None:
     66             return self._decorate_with_span('null', 'null')
     67         elif isinstance(value, list):
     68             return self._array_to_html(value)
     69         elif isinstance(value, dict):
     70             return self._object_to_html(value)
     71         elif isinstance(value, bool):
     72             return self._decorate_with_span(str(value).lower(), 'bool')
     73         elif isinstance(value, (int, float)):
     74             return self._decorate_with_span(value, 'num')
     75         else:
     76             assert isinstance(value, basestring)
     77             return self._decorate_with_span('"%s"' % value, 'string')
     78 
     79 
     80     # Convert an array into an HTML fragment
     81     def _array_to_html(self, array):
     82         if not array:
     83             return '[ ]'
     84 
     85         output = ['[<ul class="array collapsible">']
     86         for value in array:
     87             output.append('<li>')
     88             output.append(self._value_to_html(value))
     89             output.append('</li>')
     90         output.append('</ul>]')
     91         return ''.join(output)
     92 
     93 
     94     def _link_href(self, href):
     95         if '?' in href:
     96             joiner = '&amp;'
     97         else:
     98             joiner = '?'
     99         return href + joiner + 'alt=json-html'
    100 
    101 
    102     # Convert a JSON object to an HTML fragment
    103     def _object_to_html(self, json_object):
    104         if not json_object:
    105             return '{ }'
    106 
    107         output = ['{<ul class="obj collapsible">']
    108         for key, value in json_object.iteritems():
    109             assert isinstance(key, basestring)
    110             output.append('<li>')
    111             output.append('<span class="prop">%s</span>: '
    112                           % self._html_encode(key))
    113             value_html = self._value_to_html(value)
    114             if key == 'href':
    115                 assert isinstance(value, basestring)
    116                 output.append('<a href="%s">%s</a>' % (self._link_href(value),
    117                                                        value_html))
    118             else:
    119                 output.append(value_html)
    120             output.append('</li>')
    121         output.append('</ul>}')
    122         return ''.join(output)
    123 
    124 
    125     # Convert a whole JSON object into a formatted HTML document.
    126     def json_to_html(self, json_value):
    127         return _HTML_DOCUMENT_TEMPLATE % self._value_to_html(json_value)
    128 
    129 
    130 class JsonToHtmlMiddleware(object):
    131     def process_response(self, request, response):
    132         if response['Content-type'] != 'application/json':
    133             return response
    134         if request.GET.get('alt', None) != 'json-html':
    135             return response
    136 
    137         json_value = json.loads(response.content)
    138         html = JsonHtmlFormatter().json_to_html(json_value)
    139         response.content = html
    140         response['Content-type'] = 'text/html'
    141         response['Content-length'] = len(html)
    142         return response
    143