diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py index 4c963b0b79..a94a207ad6 100644 --- a/django/core/serializers/json.py +++ b/django/core/serializers/json.py @@ -37,6 +37,7 @@ class Serializer(PythonSerializer): if self.options.get('indent'): # Prevent trailing spaces self.json_kwargs['separators'] = (',', ': ') + self.json_kwargs.setdefault('cls', DjangoJSONEncoder) def start_serialization(self): self._init_options() @@ -58,8 +59,7 @@ class Serializer(PythonSerializer): self.stream.write(" ") if indent: self.stream.write("\n") - json.dump(self.get_dump_object(obj), self.stream, - cls=DjangoJSONEncoder, **self.json_kwargs) + json.dump(self.get_dump_object(obj), self.stream, **self.json_kwargs) self._current = None def getvalue(self): diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index 23af4a4d60..974e1e4adb 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -206,6 +206,10 @@ Serialization * The new ``django.core.serializers.base.Serializer.stream_class`` attribute allows subclasses to customize the default stream. +* The encoder used by the :ref:`JSON serializer ` + can now be customized by passing a ``cls`` keyword argument to the + ``serializers.serialize()`` function. + Signals ~~~~~~~ diff --git a/docs/topics/serialization.txt b/docs/topics/serialization.txt index fa5b6b33ea..c08ea27977 100644 --- a/docs/topics/serialization.txt +++ b/docs/topics/serialization.txt @@ -265,6 +265,17 @@ work:: return force_text(obj) return super(LazyEncoder, self).default(obj) +You can then pass ``cls=LazyEncoder`` to the ``serializers.serialize()`` +function:: + + from django.core.serializers import serialize + + serialize('json', SomeModel.objects.all(), cls=LazyEncoder) + +.. versionchanged:: 1.11 + + The ability to use a custom encoder using ``cls=...`` was added. + Also note that GeoDjango provides a :doc:`customized GeoJSON serializer `. diff --git a/tests/serializers/test_json.py b/tests/serializers/test_json.py index d8d445d1f9..45b3ab1527 100644 --- a/tests/serializers/test_json.py +++ b/tests/serializers/test_json.py @@ -1,13 +1,16 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import decimal import json import re from django.core import serializers from django.core.serializers.base import DeserializationError from django.core.serializers.json import DjangoJSONEncoder +from django.db import models from django.test import SimpleTestCase, TestCase, TransactionTestCase +from django.test.utils import isolate_apps from django.utils.translation import override, ugettext_lazy from .models import Score @@ -80,6 +83,23 @@ class JsonSerializerTestCase(SerializersTestBase, TestCase): if re.search(r'.+,\s*$', line): self.assertEqual(line, line.rstrip()) + @isolate_apps('serializers') + def test_custom_encoder(self): + class ScoreDecimal(models.Model): + score = models.DecimalField() + + class CustomJSONEncoder(json.JSONEncoder): + def default(self, o): + if isinstance(o, decimal.Decimal): + return str(o) + return super(CustomJSONEncoder, self).default(o) + + s = serializers.json.Serializer() + json_data = s.serialize( + [ScoreDecimal(score=decimal.Decimal(1.0))], cls=CustomJSONEncoder + ) + self.assertIn('"fields": {"score": "1"}', json_data) + def test_json_deserializer_exception(self): with self.assertRaises(DeserializationError): for obj in serializers.deserialize("json", """[{"pk":1}"""):