""" Serialize data to/from JSON """ import datetime import decimal import json import uuid from django.core.serializers.base import DeserializationError from django.core.serializers.python import ( Deserializer as PythonDeserializer, Serializer as PythonSerializer, ) from django.utils.duration import duration_iso_string from django.utils.functional import Promise from django.utils.timezone import is_aware class Serializer(PythonSerializer): """Convert a queryset to JSON.""" internal_use_only = False def _init_options(self): self._current = None self.json_kwargs = self.options.copy() self.json_kwargs.pop('stream', None) self.json_kwargs.pop('fields', None) 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() self.stream.write("[") def end_serialization(self): if self.options.get("indent"): self.stream.write("\n") self.stream.write("]") if self.options.get("indent"): self.stream.write("\n") def end_object(self, obj): # self._current has the field data indent = self.options.get("indent") if not self.first: self.stream.write(",") if not indent: self.stream.write(" ") if indent: self.stream.write("\n") json.dump(self.get_dump_object(obj), self.stream, **self.json_kwargs) self._current = None def getvalue(self): # Grandparent super return super(PythonSerializer, self).getvalue() def Deserializer(stream_or_string, **options): """Deserialize a stream or string of JSON data.""" if not isinstance(stream_or_string, (bytes, str)): stream_or_string = stream_or_string.read() if isinstance(stream_or_string, bytes): stream_or_string = stream_or_string.decode() try: objects = json.loads(stream_or_string) yield from PythonDeserializer(objects, **options) except (GeneratorExit, DeserializationError): raise except Exception as exc: raise DeserializationError() from exc class DjangoJSONEncoder(json.JSONEncoder): """ JSONEncoder subclass that knows how to encode date/time, decimal types, and UUIDs. """ def default(self, o): # See "Date Time String Format" in the ECMA-262 specification. if isinstance(o, datetime.datetime): r = o.isoformat() if o.microsecond: r = r[:23] + r[26:] if r.endswith('+00:00'): r = r[:-6] + 'Z' return r elif isinstance(o, datetime.date): return o.isoformat() elif isinstance(o, datetime.time): if is_aware(o): raise ValueError("JSON can't represent timezone-aware times.") r = o.isoformat() if o.microsecond: r = r[:12] return r elif isinstance(o, datetime.timedelta): return duration_iso_string(o) elif isinstance(o, (decimal.Decimal, uuid.UUID, Promise)): return str(o) else: return super().default(o)