diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 55aa10d..143d068 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -1,5 +1,6 @@
"""Default variable filters."""
+import json
import re
import random as random_module
import unicodedata
@@ -838,6 +839,47 @@ def filesizeformat(bytes):
return ugettext("%s TB") % filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024))
return ugettext("%s PB") % filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024 * 1024))
+def _recursive_escape(value, esc=conditional_escape):
+ """Recursively escapes strings in an object.
+
+ Traverses dict, list and tuples. These are the data structures supported
+ by the JSON encoder.
+ """
+
+ if isinstance(value, dict):
+ return type(value)((esc(k), esc(v)) for (k, v) in value.iteritems())
+ elif isinstance(value, (list, tuple)):
+ return type(value)(esc(v) for v in value)
+ elif isinstance(value, (str, unicode)):
+ return esc(value)
+ elif isinstance(value, (int, long, float)) or value in (True, False, None):
+ return value
+ # We've exhausted all the types acceptable by the default JSON encoder.
+ # Django's improved JSON encoder handles a few other types, all of which
+ # are represented by strings. For these types, we apply JSON encoding
+ # immediately and then escape the result.
+ else:
+ # Importing this at the top level causes an import loop.
+ from django.core.serializers.json import DjangoJSONEncoder
+ return esc(DjangoJSONEncoder().default(value))
+
+@register.filter("json", needs_autoescape=True)
+def json_filter(value, autoescape=None):
+ """
+ Returns the JSON representation of the value.
+ """
+ try:
+ # Importing this at the top level causes an import loop.
+ from django.core.serializers.json import DjangoJSONEncoder
+ # If value contains custom subclasses of int, str, datetime, etc.
+ # arbitrary exceptions may be raised during escaping or serialization.
+ if autoescape:
+ value = _recursive_escape(value)
+ result = json.dumps(value, cls=DjangoJSONEncoder)
+ except Exception:
+ return ''
+ return mark_safe(result)
+
@register.filter(is_safe=False)
def pluralize(value, arg=u's'):
"""
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 5019963..112e0a2 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -1602,6 +1602,22 @@ For example::
If ``value`` is the list ``['a', 'b', 'c']``, the output will be the string
``"a // b // c"``.
+.. templatefilter:: json
+
+json
+^^^^
+
+.. versionadded:: 1.5
+
+Converts a Python data structure into a JSON string.
+
+For example::
+
+ {{ value|json }}
+
+If ``value`` is the Python dict ``{u'hello': (u'world', u'wide')}``, the
+output will be the string ``'{"hello": ["world", "wide"]}'``.
+
.. templatefilter:: last
last
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
index 5a92e7d..70b3f4b 100644
--- a/docs/releases/1.5.txt
+++ b/docs/releases/1.5.txt
@@ -33,6 +33,15 @@ version compatible with Python 2.6.
What's new in Django 1.5
========================
+``json`` template filter
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+This new filter turns an arbitrary Python object into a JSON string. It's
+handy to pass data to JavaScript without resorting to an AJAX call. It's also
+useful when both your HTML and your JavaScript use the same data, for instance
+when you're displaying a graph and the corresponding data table. See the
+documentation for :tfilter:`json` for more information.
+
Minor features
~~~~~~~~~~~~~~
diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py
index 41f8e43..ef0918f 100644
--- a/tests/regressiontests/templates/filters.py
+++ b/tests/regressiontests/templates/filters.py
@@ -106,6 +106,12 @@ def get_filter_tests():
'filter-lower01': ("{% autoescape off %}{{ a|lower }} {{ b|lower }}{% endautoescape %}", {"a": "Apple & banana", "b": mark_safe("Apple & banana")}, u"apple & banana apple & banana"),
'filter-lower02': ("{{ a|lower }} {{ b|lower }}", {"a": "Apple & banana", "b": mark_safe("Apple & banana")}, u"apple & banana apple & banana"),
+ # The output of "json" is escaped according to the current
+ # autoescape setting
+ 'filter-json01': ("{{ data|json }}", {"data": "