Upgraded included simplejson to 2.0.7.

Also changed importing logic to prefer a system-installed version of
simplejson (unless it's an earlier version that does not contian the C
speedups), then the json module from Python 2.6, then the version
shipped with Django.

Fixed #9266.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@9707 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2009-01-06 05:13:02 +00:00
parent 3b489b7742
commit a9c2f033cd
6 changed files with 725 additions and 715 deletions

View File

@ -1,376 +1,343 @@
r""" r"""JSON (JavaScript Object Notation) <http://json.org> is a subset of
A simple, fast, extensible JSON encoder and decoder
JSON (JavaScript Object Notation) <http://json.org> is a subset of
JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
interchange format. interchange format.
simplejson exposes an API familiar to uses of the standard library :mod:`simplejson` exposes an API familiar to users of the standard library
marshal and pickle modules. :mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
version of the :mod:`json` library contained in Python 2.6, but maintains
compatibility with Python 2.4 and Python 2.5 and (currently) has
significant performance advantages, even without using the optional C
extension for speedups.
Encoding basic Python object hierarchies:: Encoding basic Python object hierarchies::
>>> import simplejson >>> import simplejson as json
>>> simplejson.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}]) >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
'["foo", {"bar": ["baz", null, 1.0, 2]}]' '["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> print simplejson.dumps("\"foo\bar") >>> print json.dumps("\"foo\bar")
"\"foo\bar" "\"foo\bar"
>>> print simplejson.dumps(u'\u1234') >>> print json.dumps(u'\u1234')
"\u1234" "\u1234"
>>> print simplejson.dumps('\\') >>> print json.dumps('\\')
"\\" "\\"
>>> print simplejson.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True) >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
{"a": 0, "b": 0, "c": 0} {"a": 0, "b": 0, "c": 0}
>>> from StringIO import StringIO >>> from StringIO import StringIO
>>> io = StringIO() >>> io = StringIO()
>>> simplejson.dump(['streaming API'], io) >>> json.dump(['streaming API'], io)
>>> io.getvalue() >>> io.getvalue()
'["streaming API"]' '["streaming API"]'
Compact encoding:: Compact encoding::
>>> import simplejson >>> import simplejson as json
>>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':')) >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
'[1,2,3,{"4":5,"6":7}]' '[1,2,3,{"4":5,"6":7}]'
Pretty printing:: Pretty printing::
>>> import simplejson >>> import simplejson as json
>>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4) >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
>>> print '\n'.join([l.rstrip() for l in s.splitlines()])
{ {
"4": 5, "4": 5,
"6": 7 "6": 7
} }
Decoding JSON:: Decoding JSON::
>>> import simplejson >>> import simplejson as json
>>> simplejson.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
[u'foo', {u'bar': [u'baz', None, 1.0, 2]}] >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj
>>> simplejson.loads('"\\"foo\\bar"') True
u'"foo\x08ar' >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar'
True
>>> from StringIO import StringIO >>> from StringIO import StringIO
>>> io = StringIO('["streaming API"]') >>> io = StringIO('["streaming API"]')
>>> simplejson.load(io) >>> json.load(io)[0] == 'streaming API'
[u'streaming API'] True
Specializing JSON object decoding:: Specializing JSON object decoding::
>>> import simplejson >>> import simplejson as json
>>> def as_complex(dct): >>> def as_complex(dct):
... if '__complex__' in dct: ... if '__complex__' in dct:
... return complex(dct['real'], dct['imag']) ... return complex(dct['real'], dct['imag'])
... return dct ... return dct
... ...
>>> simplejson.loads('{"__complex__": true, "real": 1, "imag": 2}', >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
... object_hook=as_complex) ... object_hook=as_complex)
(1+2j) (1+2j)
>>> import decimal >>> import decimal
>>> simplejson.loads('1.1', parse_float=decimal.Decimal) >>> json.loads('1.1', parse_float=decimal.Decimal) == decimal.Decimal('1.1')
Decimal("1.1") True
Extending JSONEncoder:: Specializing JSON object encoding::
>>> import simplejson >>> import simplejson as json
>>> class ComplexEncoder(simplejson.JSONEncoder): >>> def encode_complex(obj):
... def default(self, obj): ... if isinstance(obj, complex):
... if isinstance(obj, complex): ... return [obj.real, obj.imag]
... return [obj.real, obj.imag] ... raise TypeError("%r is not JSON serializable" % (o,))
... return simplejson.JSONEncoder.default(self, obj) ...
... >>> json.dumps(2 + 1j, default=encode_complex)
>>> dumps(2 + 1j, cls=ComplexEncoder) '[2.0, 1.0]'
'[2.0, 1.0]' >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j)
>>> ComplexEncoder().encode(2 + 1j) '[2.0, 1.0]'
'[2.0, 1.0]' >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j))
>>> list(ComplexEncoder().iterencode(2 + 1j)) '[2.0, 1.0]'
['[', '2.0', ', ', '1.0', ']']
Using simplejson.tool from the shell to validate and pretty-print::
Using simplejson from the shell to validate and
pretty-print::
$ echo '{"json":"obj"}' | python -msimplejson.tool $ echo '{"json":"obj"}' | python -msimplejson.tool
{ {
"json": "obj" "json": "obj"
} }
$ echo '{ 1.2:3.4}' | python -msimplejson.tool $ echo '{ 1.2:3.4}' | python -msimplejson.tool
Expecting property name: line 1 column 2 (char 2) Expecting property name: line 1 column 2 (char 2)
Note that the JSON produced by this module's default settings
is a subset of YAML, so it may be used as a serializer for that as well.
""" """
__version__ = '1.9.2'
__all__ = [
'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONEncoder',
]
if __name__ == '__main__': # Django modification: try to use the system version first, providing it's
import warnings # either of a later version of has the C speedups in place. Otherwise, fall
warnings.warn('python -msimplejson is deprecated, use python -msiplejson.tool', DeprecationWarning) # back to our local copy.
__version__ = '2.0.7'
use_system_version = False
try:
# The system-installed version has priority providing it is either not an
# earlier version or it contains the C speedups.
import simplejson
if (simplejson.__version__.split('.') >= __version__.split('.') or
hasattr(simplejson, '_speedups')):
from simplejson import *
use_system_version = True
except ImportError:
pass
if not use_system_version:
try:
from json import * # Python 2.6 preferred over local copy.
use_system_version = True
except ImportError:
pass
# If all else fails, we have a bundled version that can be used.
if not use_system_version:
__all__ = [
'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONEncoder',
]
from django.utils.simplejson.decoder import JSONDecoder from django.utils.simplejson.decoder import JSONDecoder
from django.utils.simplejson.encoder import JSONEncoder from django.utils.simplejson.encoder import JSONEncoder
else:
from decoder import JSONDecoder
from encoder import JSONEncoder
_default_encoder = JSONEncoder( _default_encoder = JSONEncoder(
skipkeys=False, skipkeys=False,
ensure_ascii=True, ensure_ascii=True,
check_circular=True, check_circular=True,
allow_nan=True, allow_nan=True,
indent=None, indent=None,
separators=None, separators=None,
encoding='utf-8', encoding='utf-8',
default=None, default=None,
) )
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None, allow_nan=True, cls=None, indent=None, separators=None,
encoding='utf-8', default=None, **kw): encoding='utf-8', default=None, **kw):
""" """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object).
``.write()``-supporting file-like object).
If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
(``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``) (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
will be skipped instead of raising a ``TypeError``. will be skipped instead of raising a ``TypeError``.
If ``ensure_ascii`` is ``False``, then the some chunks written to ``fp`` If ``ensure_ascii`` is ``False``, then the some chunks written to ``fp``
may be ``unicode`` instances, subject to normal Python ``str`` to may be ``unicode`` instances, subject to normal Python ``str`` to
``unicode`` coercion rules. Unless ``fp.write()`` explicitly ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
understands ``unicode`` (as in ``codecs.getwriter()``) this is likely understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
to cause an error. to cause an error.
If ``check_circular`` is ``False``, then the circular reference check If ``check_circular`` is ``False``, then the circular reference check
for container types will be skipped and a circular reference will for container types will be skipped and a circular reference will
result in an ``OverflowError`` (or worse). result in an ``OverflowError`` (or worse).
If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
in strict compliance of the JSON specification, instead of using the in strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
If ``indent`` is a non-negative integer, then JSON array elements and object If ``indent`` is a non-negative integer, then JSON array elements and object
members will be pretty-printed with that indent level. An indent level members will be pretty-printed with that indent level. An indent level
of 0 will only insert newlines. ``None`` is the most compact representation. of 0 will only insert newlines. ``None`` is the most compact representation.
If ``separators`` is an ``(item_separator, dict_separator)`` tuple If ``separators`` is an ``(item_separator, dict_separator)`` tuple
then it will be used instead of the default ``(', ', ': ')`` separators. then it will be used instead of the default ``(', ', ': ')`` separators.
``(',', ':')`` is the most compact JSON representation. ``(',', ':')`` is the most compact JSON representation.
``encoding`` is the character encoding for str instances, default is UTF-8. ``encoding`` is the character encoding for str instances, default is UTF-8.
``default(obj)`` is a function that should return a serializable version ``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError. of obj or raise TypeError. The default simply raises TypeError.
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with ``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg. the ``cls`` kwarg.
"""
# cached encoder """
if (skipkeys is False and ensure_ascii is True and # cached encoder
check_circular is True and allow_nan is True and if (skipkeys is False and ensure_ascii is True and
cls is None and indent is None and separators is None and check_circular is True and allow_nan is True and
encoding == 'utf-8' and default is None and not kw): cls is None and indent is None and separators is None and
iterable = _default_encoder.iterencode(obj) encoding == 'utf-8' and default is None and not kw):
else: iterable = _default_encoder.iterencode(obj)
else:
if cls is None:
cls = JSONEncoder
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators, encoding=encoding,
default=default, **kw).iterencode(obj)
# could accelerate with writelines in some versions of Python, at
# a debuggability cost
for chunk in iterable:
fp.write(chunk)
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
encoding='utf-8', default=None, **kw):
"""Serialize ``obj`` to a JSON formatted ``str``.
If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
(``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
will be skipped instead of raising a ``TypeError``.
If ``ensure_ascii`` is ``False``, then the return value will be a
``unicode`` instance subject to normal Python ``str`` to ``unicode``
coercion rules instead of being escaped to an ASCII ``str``.
If ``check_circular`` is ``False``, then the circular reference check
for container types will be skipped and a circular reference will
result in an ``OverflowError`` (or worse).
If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
If ``indent`` is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level. An indent
level of 0 will only insert newlines. ``None`` is the most compact
representation.
If ``separators`` is an ``(item_separator, dict_separator)`` tuple
then it will be used instead of the default ``(', ', ': ')`` separators.
``(',', ':')`` is the most compact JSON representation.
``encoding`` is the character encoding for str instances, default is UTF-8.
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg.
"""
# cached encoder
if (skipkeys is False and ensure_ascii is True and
check_circular is True and allow_nan is True and
cls is None and indent is None and separators is None and
encoding == 'utf-8' and default is None and not kw):
return _default_encoder.encode(obj)
if cls is None: if cls is None:
cls = JSONEncoder cls = JSONEncoder
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, return cls(
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent, check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators, encoding=encoding, separators=separators, encoding=encoding, default=default,
default=default, **kw).iterencode(obj) **kw).encode(obj)
# could accelerate with writelines in some versions of Python, at
# a debuggability cost
for chunk in iterable:
fp.write(chunk)
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, _default_decoder = JSONDecoder(encoding=None, object_hook=None)
allow_nan=True, cls=None, indent=None, separators=None,
encoding='utf-8', default=None, **kw):
"""
Serialize ``obj`` to a JSON formatted ``str``.
If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
(``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
will be skipped instead of raising a ``TypeError``.
If ``ensure_ascii`` is ``False``, then the return value will be a
``unicode`` instance subject to normal Python ``str`` to ``unicode``
coercion rules instead of being escaped to an ASCII ``str``.
If ``check_circular`` is ``False``, then the circular reference check
for container types will be skipped and a circular reference will
result in an ``OverflowError`` (or worse).
If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
If ``indent`` is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level. An indent
level of 0 will only insert newlines. ``None`` is the most compact
representation.
If ``separators`` is an ``(item_separator, dict_separator)`` tuple
then it will be used instead of the default ``(', ', ': ')`` separators.
``(',', ':')`` is the most compact JSON representation.
``encoding`` is the character encoding for str instances, default is UTF-8.
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg.
"""
# cached encoder
if (skipkeys is False and ensure_ascii is True and
check_circular is True and allow_nan is True and
cls is None and indent is None and separators is None and
encoding == 'utf-8' and default is None and not kw):
return _default_encoder.encode(obj)
if cls is None:
cls = JSONEncoder
return cls(
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators, encoding=encoding, default=default,
**kw).encode(obj)
_default_decoder = JSONDecoder(encoding=None, object_hook=None) def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
parse_int=None, parse_constant=None, **kw):
"""Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
a JSON document) to a Python object.
If the contents of ``fp`` is encoded with an ASCII based encoding other
than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must
be specified. Encodings that are not ASCII based (such as UCS-2) are
not allowed, and should be wrapped with
``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode``
object and passed to ``loads()``
``object_hook`` is an optional function that will be called with the
result of any object literal decode (a ``dict``). The return value of
``object_hook`` will be used instead of the ``dict``. This feature
can be used to implement custom decoders (e.g. JSON-RPC class hinting).
To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
kwarg.
"""
return loads(fp.read(),
encoding=encoding, cls=cls, object_hook=object_hook,
parse_float=parse_float, parse_int=parse_int,
parse_constant=parse_constant, **kw)
def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None, def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
parse_int=None, parse_constant=None, **kw): parse_int=None, parse_constant=None, **kw):
""" """Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
Deserialize ``fp`` (a ``.read()``-supporting file-like object containing document) to a Python object.
a JSON document) to a Python object.
If the contents of ``fp`` is encoded with an ASCII based encoding other If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding
than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name
be specified. Encodings that are not ASCII based (such as UCS-2) are must be specified. Encodings that are not ASCII based (such as UCS-2)
not allowed, and should be wrapped with are not allowed and should be decoded to ``unicode`` first.
``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode``
object and passed to ``loads()``
``object_hook`` is an optional function that will be called with the ``object_hook`` is an optional function that will be called with the
result of any object literal decode (a ``dict``). The return value of result of any object literal decode (a ``dict``). The return value of
``object_hook`` will be used instead of the ``dict``. This feature ``object_hook`` will be used instead of the ``dict``. This feature
can be used to implement custom decoders (e.g. JSON-RPC class hinting). can be used to implement custom decoders (e.g. JSON-RPC class hinting).
To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
kwarg.
"""
return loads(fp.read(),
encoding=encoding, cls=cls, object_hook=object_hook,
parse_float=parse_float, parse_int=parse_int,
parse_constant=parse_constant, **kw)
``parse_float``, if specified, will be called with the string
of every JSON float to be decoded. By default this is equivalent to
float(num_str). This can be used to use another datatype or parser
for JSON floats (e.g. decimal.Decimal).
def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, ``parse_int``, if specified, will be called with the string
parse_int=None, parse_constant=None, **kw): of every JSON int to be decoded. By default this is equivalent to
""" int(num_str). This can be used to use another datatype or parser
Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON for JSON integers (e.g. float).
document) to a Python object.
If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding ``parse_constant``, if specified, will be called with one of the
other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name following strings: -Infinity, Infinity, NaN, null, true, false.
must be specified. Encodings that are not ASCII based (such as UCS-2) This can be used to raise an exception if invalid JSON numbers
are not allowed and should be decoded to ``unicode`` first. are encountered.
``object_hook`` is an optional function that will be called with the To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
result of any object literal decode (a ``dict``). The return value of kwarg.
``object_hook`` will be used instead of the ``dict``. This feature
can be used to implement custom decoders (e.g. JSON-RPC class hinting).
``parse_float``, if specified, will be called with the string """
of every JSON float to be decoded. By default this is equivalent to if (cls is None and encoding is None and object_hook is None and
float(num_str). This can be used to use another datatype or parser parse_int is None and parse_float is None and
for JSON floats (e.g. decimal.Decimal). parse_constant is None and not kw):
return _default_decoder.decode(s)
``parse_int``, if specified, will be called with the string if cls is None:
of every JSON int to be decoded. By default this is equivalent to cls = JSONDecoder
int(num_str). This can be used to use another datatype or parser if object_hook is not None:
for JSON integers (e.g. float). kw['object_hook'] = object_hook
if parse_float is not None:
``parse_constant``, if specified, will be called with one of the kw['parse_float'] = parse_float
following strings: -Infinity, Infinity, NaN, null, true, false. if parse_int is not None:
This can be used to raise an exception if invalid JSON numbers kw['parse_int'] = parse_int
are encountered. if parse_constant is not None:
kw['parse_constant'] = parse_constant
To use a custom ``JSONDecoder`` subclass, specify it with the ``cls`` return cls(encoding=encoding, **kw).decode(s)
kwarg.
"""
if (cls is None and encoding is None and object_hook is None and
parse_int is None and parse_float is None and
parse_constant is None and not kw):
return _default_decoder.decode(s)
if cls is None:
cls = JSONDecoder
if object_hook is not None:
kw['object_hook'] = object_hook
if parse_float is not None:
kw['parse_float'] = parse_float
if parse_int is not None:
kw['parse_int'] = parse_int
if parse_constant is not None:
kw['parse_constant'] = parse_constant
return cls(encoding=encoding, **kw).decode(s)
#
# Compatibility cruft from other libraries
#
def decode(s):
"""
demjson, python-cjson API compatibility hook. Use loads(s) instead.
"""
import warnings
warnings.warn("simplejson.loads(s) should be used instead of decode(s)",
DeprecationWarning)
return loads(s)
def encode(obj):
"""
demjson, python-cjson compatibility hook. Use dumps(s) instead.
"""
import warnings
warnings.warn("simplejson.dumps(s) should be used instead of encode(s)",
DeprecationWarning)
return dumps(obj)
def read(s):
"""
jsonlib, JsonUtils, python-json, json-py API compatibility hook.
Use loads(s) instead.
"""
import warnings
warnings.warn("simplejson.loads(s) should be used instead of read(s)",
DeprecationWarning)
return loads(s)
def write(obj):
"""
jsonlib, JsonUtils, python-json, json-py API compatibility hook.
Use dumps(s) instead.
"""
import warnings
warnings.warn("simplejson.dumps(s) should be used instead of write(s)",
DeprecationWarning)
return dumps(obj)
if __name__ == '__main__':
import simplejson.tool
simplejson.tool.main()

View File

@ -1,20 +1,17 @@
""" """Implementation of JSONDecoder
Implementation of JSONDecoder
""" """
import re import re
import sys import sys
import struct
from django.utils.simplejson.scanner import Scanner, pattern from django.utils.simplejson.scanner import make_scanner
try: c_scanstring = None
from django.utils.simplejson._speedups import scanstring as c_scanstring
except ImportError: __all__ = ['JSONDecoder']
pass
FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
def _floatconstants(): def _floatconstants():
import struct
import sys
_BYTES = '7FF80000000000007FF0000000000000'.decode('hex') _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
if sys.byteorder != 'big': if sys.byteorder != 'big':
_BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1] _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
@ -34,6 +31,7 @@ def linecol(doc, pos):
def errmsg(msg, doc, pos, end=None): def errmsg(msg, doc, pos, end=None):
# Note that this function is called from _speedups
lineno, colno = linecol(doc, pos) lineno, colno = linecol(doc, pos)
if end is None: if end is None:
return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos) return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos)
@ -46,35 +44,8 @@ _CONSTANTS = {
'-Infinity': NegInf, '-Infinity': NegInf,
'Infinity': PosInf, 'Infinity': PosInf,
'NaN': NaN, 'NaN': NaN,
'true': True,
'false': False,
'null': None,
} }
def JSONConstant(match, context, c=_CONSTANTS):
s = match.group(0)
fn = getattr(context, 'parse_constant', None)
if fn is None:
rval = c[s]
else:
rval = fn(s)
return rval, None
pattern('(-?Infinity|NaN|true|false|null)')(JSONConstant)
def JSONNumber(match, context):
match = JSONNumber.regex.match(match.string, *match.span())
integer, frac, exp = match.groups()
if frac or exp:
fn = getattr(context, 'parse_float', None) or float
res = fn(integer + (frac or '') + (exp or ''))
else:
fn = getattr(context, 'parse_int', None) or int
res = fn(integer)
return res, None
pattern(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?')(JSONNumber)
STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS) STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
BACKSLASH = { BACKSLASH = {
'"': u'"', '\\': u'\\', '/': u'/', '"': u'"', '\\': u'\\', '/': u'/',
@ -84,6 +55,14 @@ BACKSLASH = {
DEFAULT_ENCODING = "utf-8" DEFAULT_ENCODING = "utf-8"
def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match): def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match):
"""Scan the string s for a JSON string. End is the index of the
character in s after the quote that started the JSON string.
Unescapes all valid JSON string escape sequences and raises ValueError
on attempt to decode an invalid string. If strict is False then literal
control characters are allowed in the string.
Returns a tuple of the decoded string and the index of the character in s
after the end quote."""
if encoding is None: if encoding is None:
encoding = DEFAULT_ENCODING encoding = DEFAULT_ENCODING
chunks = [] chunks = []
@ -96,15 +75,19 @@ def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHU
errmsg("Unterminated string starting at", s, begin)) errmsg("Unterminated string starting at", s, begin))
end = chunk.end() end = chunk.end()
content, terminator = chunk.groups() content, terminator = chunk.groups()
# Content is contains zero or more unescaped string characters
if content: if content:
if not isinstance(content, unicode): if not isinstance(content, unicode):
content = unicode(content, encoding) content = unicode(content, encoding)
_append(content) _append(content)
# Terminator is the end of string, a literal control character,
# or a backslash denoting that an escape sequence follows
if terminator == '"': if terminator == '"':
break break
elif terminator != '\\': elif terminator != '\\':
if strict: if strict:
raise ValueError(errmsg("Invalid control character %r at", s, end)) msg = "Invalid control character %r at" % (terminator,)
raise ValueError(msg, s, end)
else: else:
_append(terminator) _append(terminator)
continue continue
@ -113,142 +96,162 @@ def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHU
except IndexError: except IndexError:
raise ValueError( raise ValueError(
errmsg("Unterminated string starting at", s, begin)) errmsg("Unterminated string starting at", s, begin))
# If not a unicode escape sequence, must be in the lookup table
if esc != 'u': if esc != 'u':
try: try:
m = _b[esc] char = _b[esc]
except KeyError: except KeyError:
raise ValueError( raise ValueError(
errmsg("Invalid \\escape: %r" % (esc,), s, end)) errmsg("Invalid \\escape: %r" % (esc,), s, end))
end += 1 end += 1
else: else:
# Unicode escape sequence
esc = s[end + 1:end + 5] esc = s[end + 1:end + 5]
next_end = end + 5 next_end = end + 5
msg = "Invalid \\uXXXX escape" if len(esc) != 4:
try: msg = "Invalid \\uXXXX escape"
if len(esc) != 4:
raise ValueError
uni = int(esc, 16)
if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
if not s[end + 5:end + 7] == '\\u':
raise ValueError
esc2 = s[end + 7:end + 11]
if len(esc2) != 4:
raise ValueError
uni2 = int(esc2, 16)
uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
next_end += 6
m = unichr(uni)
except ValueError:
raise ValueError(errmsg(msg, s, end)) raise ValueError(errmsg(msg, s, end))
uni = int(esc, 16)
# Check for surrogate pair on UCS-4 systems
if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
if not s[end + 5:end + 7] == '\\u':
raise ValueError(errmsg(msg, s, end))
esc2 = s[end + 7:end + 11]
if len(esc2) != 4:
raise ValueError(errmsg(msg, s, end))
uni2 = int(esc2, 16)
uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
next_end += 6
char = unichr(uni)
end = next_end end = next_end
_append(m) # Append the unescaped character
_append(char)
return u''.join(chunks), end return u''.join(chunks), end
# Use speedup # Use speedup if available
try: scanstring = c_scanstring or py_scanstring
scanstring = c_scanstring
except NameError:
scanstring = py_scanstring
def JSONString(match, context): WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
encoding = getattr(context, 'encoding', None) WHITESPACE_STR = ' \t\n\r'
strict = getattr(context, 'strict', True)
return scanstring(match.string, match.end(), encoding, strict)
pattern(r'"')(JSONString)
def JSONObject((s, end), encoding, strict, scan_once, object_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
WHITESPACE = re.compile(r'\s*', FLAGS)
def JSONObject(match, context, _w=WHITESPACE.match):
pairs = {} pairs = {}
s = match.string # Use a slice to prevent IndexError from being raised, the following
end = _w(s, match.end()).end() # check will raise a more specific ValueError if the string is empty
nextchar = s[end:end + 1] nextchar = s[end:end + 1]
# Trivial empty object # Normally we expect nextchar == '"'
if nextchar == '}':
return pairs, end + 1
if nextchar != '"': if nextchar != '"':
raise ValueError(errmsg("Expecting property name", s, end)) if nextchar in _ws:
end = _w(s, end).end()
nextchar = s[end:end + 1]
# Trivial empty object
if nextchar == '}':
return pairs, end + 1
elif nextchar != '"':
raise ValueError(errmsg("Expecting property name", s, end))
end += 1 end += 1
encoding = getattr(context, 'encoding', None)
strict = getattr(context, 'strict', True)
iterscan = JSONScanner.iterscan
while True: while True:
key, end = scanstring(s, end, encoding, strict) key, end = scanstring(s, end, encoding, strict)
end = _w(s, end).end()
# To skip some function call overhead we optimize the fast paths where
# the JSON key separator is ": " or just ":".
if s[end:end + 1] != ':': if s[end:end + 1] != ':':
raise ValueError(errmsg("Expecting : delimiter", s, end)) end = _w(s, end).end()
end = _w(s, end + 1).end() if s[end:end + 1] != ':':
raise ValueError(errmsg("Expecting : delimiter", s, end))
end += 1
try: try:
value, end = iterscan(s, idx=end, context=context).next() if s[end] in _ws:
end += 1
if s[end] in _ws:
end = _w(s, end + 1).end()
except IndexError:
pass
try:
value, end = scan_once(s, end)
except StopIteration: except StopIteration:
raise ValueError(errmsg("Expecting object", s, end)) raise ValueError(errmsg("Expecting object", s, end))
pairs[key] = value pairs[key] = value
end = _w(s, end).end()
nextchar = s[end:end + 1] try:
nextchar = s[end]
if nextchar in _ws:
end = _w(s, end + 1).end()
nextchar = s[end]
except IndexError:
nextchar = ''
end += 1 end += 1
if nextchar == '}': if nextchar == '}':
break break
if nextchar != ',': elif nextchar != ',':
raise ValueError(errmsg("Expecting , delimiter", s, end - 1)) raise ValueError(errmsg("Expecting , delimiter", s, end - 1))
end = _w(s, end).end()
nextchar = s[end:end + 1] try:
nextchar = s[end]
if nextchar in _ws:
end += 1
nextchar = s[end]
if nextchar in _ws:
end = _w(s, end + 1).end()
nextchar = s[end]
except IndexError:
nextchar = ''
end += 1 end += 1
if nextchar != '"': if nextchar != '"':
raise ValueError(errmsg("Expecting property name", s, end - 1)) raise ValueError(errmsg("Expecting property name", s, end - 1))
object_hook = getattr(context, 'object_hook', None)
if object_hook is not None: if object_hook is not None:
pairs = object_hook(pairs) pairs = object_hook(pairs)
return pairs, end return pairs, end
pattern(r'{')(JSONObject)
def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
def JSONArray(match, context, _w=WHITESPACE.match):
values = [] values = []
s = match.string
end = _w(s, match.end()).end()
# Look-ahead for trivial empty array
nextchar = s[end:end + 1] nextchar = s[end:end + 1]
if nextchar in _ws:
end = _w(s, end + 1).end()
nextchar = s[end:end + 1]
# Look-ahead for trivial empty array
if nextchar == ']': if nextchar == ']':
return values, end + 1 return values, end + 1
iterscan = JSONScanner.iterscan _append = values.append
while True: while True:
try: try:
value, end = iterscan(s, idx=end, context=context).next() value, end = scan_once(s, end)
except StopIteration: except StopIteration:
raise ValueError(errmsg("Expecting object", s, end)) raise ValueError(errmsg("Expecting object", s, end))
values.append(value) _append(value)
end = _w(s, end).end()
nextchar = s[end:end + 1] nextchar = s[end:end + 1]
if nextchar in _ws:
end = _w(s, end + 1).end()
nextchar = s[end:end + 1]
end += 1 end += 1
if nextchar == ']': if nextchar == ']':
break break
if nextchar != ',': elif nextchar != ',':
raise ValueError(errmsg("Expecting , delimiter", s, end)) raise ValueError(errmsg("Expecting , delimiter", s, end))
end = _w(s, end).end()
try:
if s[end] in _ws:
end += 1
if s[end] in _ws:
end = _w(s, end + 1).end()
except IndexError:
pass
return values, end return values, end
pattern(r'\[')(JSONArray)
ANYTHING = [
JSONObject,
JSONArray,
JSONString,
JSONConstant,
JSONNumber,
]
JSONScanner = Scanner(ANYTHING)
class JSONDecoder(object): class JSONDecoder(object):
""" """Simple JSON <http://json.org> decoder
Simple JSON <http://json.org> decoder
Performs the following translations in decoding by default: Performs the following translations in decoding by default:
+---------------+-------------------+ +---------------+-------------------+
| JSON | Python | | JSON | Python |
+===============+===================+ +===============+===================+
@ -271,18 +274,15 @@ class JSONDecoder(object):
It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
their corresponding ``float`` values, which is outside the JSON spec. their corresponding ``float`` values, which is outside the JSON spec.
"""
_scanner = Scanner(ANYTHING) """
__all__ = ['__init__', 'decode', 'raw_decode']
def __init__(self, encoding=None, object_hook=None, parse_float=None, def __init__(self, encoding=None, object_hook=None, parse_float=None,
parse_int=None, parse_constant=None, strict=True): parse_int=None, parse_constant=None, strict=True):
""" """``encoding`` determines the encoding used to interpret any ``str``
``encoding`` determines the encoding used to interpret any ``str``
objects decoded by this instance (utf-8 by default). It has no objects decoded by this instance (utf-8 by default). It has no
effect when decoding ``unicode`` objects. effect when decoding ``unicode`` objects.
Note that currently only encodings that are a superset of ASCII work, Note that currently only encodings that are a superset of ASCII work,
strings of other encodings should be passed in as ``unicode``. strings of other encodings should be passed in as ``unicode``.
@ -302,21 +302,26 @@ class JSONDecoder(object):
for JSON integers (e.g. float). for JSON integers (e.g. float).
``parse_constant``, if specified, will be called with one of the ``parse_constant``, if specified, will be called with one of the
following strings: -Infinity, Infinity, NaN, null, true, false. following strings: -Infinity, Infinity, NaN.
This can be used to raise an exception if invalid JSON numbers This can be used to raise an exception if invalid JSON numbers
are encountered. are encountered.
""" """
self.encoding = encoding self.encoding = encoding
self.object_hook = object_hook self.object_hook = object_hook
self.parse_float = parse_float self.parse_float = parse_float or float
self.parse_int = parse_int self.parse_int = parse_int or int
self.parse_constant = parse_constant self.parse_constant = parse_constant or _CONSTANTS.__getitem__
self.strict = strict self.strict = strict
self.parse_object = JSONObject
self.parse_array = JSONArray
self.parse_string = scanstring
self.scan_once = make_scanner(self)
def decode(self, s, _w=WHITESPACE.match): def decode(self, s, _w=WHITESPACE.match):
""" """Return the Python representation of ``s`` (a ``str`` or ``unicode``
Return the Python representation of ``s`` (a ``str`` or ``unicode``
instance containing a JSON document) instance containing a JSON document)
""" """
obj, end = self.raw_decode(s, idx=_w(s, 0).end()) obj, end = self.raw_decode(s, idx=_w(s, 0).end())
end = _w(s, end).end() end = _w(s, end).end()
@ -324,20 +329,17 @@ class JSONDecoder(object):
raise ValueError(errmsg("Extra data", s, end, len(s))) raise ValueError(errmsg("Extra data", s, end, len(s)))
return obj return obj
def raw_decode(self, s, **kw): def raw_decode(self, s, idx=0):
""" """Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
with a JSON document) and return a 2-tuple of the Python with a JSON document) and return a 2-tuple of the Python
representation and the index in ``s`` where the document ended. representation and the index in ``s`` where the document ended.
This can be used to decode a JSON document from a string that may This can be used to decode a JSON document from a string that may
have extraneous data at the end. have extraneous data at the end.
""" """
kw.setdefault('context', self)
try: try:
obj, end = self._scanner.iterscan(s, **kw).next() obj, end = self.scan_once(s, idx)
except StopIteration: except StopIteration:
raise ValueError("No JSON object could be decoded") raise ValueError("No JSON object could be decoded")
return obj, end return obj, end
__all__ = ['JSONDecoder']

View File

@ -1,12 +1,9 @@
""" """Implementation of JSONEncoder
Implementation of JSONEncoder
""" """
import re import re
try: c_encode_basestring_ascii = None
from django.utils.simplejson._speedups import encode_basestring_ascii as c_encode_basestring_ascii c_make_encoder = None
except ImportError:
pass
ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]') ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]')
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
@ -27,29 +24,9 @@ for i in range(0x20):
INFINITY = float('1e66666') INFINITY = float('1e66666')
FLOAT_REPR = repr FLOAT_REPR = repr
def floatstr(o, allow_nan=True):
# Check for specials. Note that this type of test is processor- and/or
# platform-specific, so do tests which don't depend on the internals.
if o != o:
text = 'NaN'
elif o == INFINITY:
text = 'Infinity'
elif o == -INFINITY:
text = '-Infinity'
else:
return FLOAT_REPR(o)
if not allow_nan:
raise ValueError("Out of range float values are not JSON compliant: %r"
% (o,))
return text
def encode_basestring(s): def encode_basestring(s):
""" """Return a JSON representation of a Python string
Return a JSON representation of a Python string
""" """
def replace(match): def replace(match):
return ESCAPE_DCT[match.group(0)] return ESCAPE_DCT[match.group(0)]
@ -57,6 +34,9 @@ def encode_basestring(s):
def py_encode_basestring_ascii(s): def py_encode_basestring_ascii(s):
"""Return an ASCII-only JSON representation of a Python string
"""
if isinstance(s, str) and HAS_UTF8.search(s) is not None: if isinstance(s, str) and HAS_UTF8.search(s) is not None:
s = s.decode('utf-8') s = s.decode('utf-8')
def replace(match): def replace(match):
@ -76,18 +56,13 @@ def py_encode_basestring_ascii(s):
return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"' return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
try: encode_basestring_ascii = c_encode_basestring_ascii or py_encode_basestring_ascii
encode_basestring_ascii = c_encode_basestring_ascii
except NameError:
encode_basestring_ascii = py_encode_basestring_ascii
class JSONEncoder(object): class JSONEncoder(object):
""" """Extensible JSON <http://json.org> encoder for Python data structures.
Extensible JSON <http://json.org> encoder for Python data structures.
Supports the following objects and types by default: Supports the following objects and types by default:
+-------------------+---------------+ +-------------------+---------------+
| Python | JSON | | Python | JSON |
+===================+===============+ +===================+===============+
@ -110,15 +85,14 @@ class JSONEncoder(object):
``.default()`` method with another method that returns a serializable ``.default()`` method with another method that returns a serializable
object for ``o`` if possible, otherwise it should call the superclass object for ``o`` if possible, otherwise it should call the superclass
implementation (to raise ``TypeError``). implementation (to raise ``TypeError``).
""" """
__all__ = ['__init__', 'default', 'encode', 'iterencode']
item_separator = ', ' item_separator = ', '
key_separator = ': ' key_separator = ': '
def __init__(self, skipkeys=False, ensure_ascii=True, def __init__(self, skipkeys=False, ensure_ascii=True,
check_circular=True, allow_nan=True, sort_keys=False, check_circular=True, allow_nan=True, sort_keys=False,
indent=None, separators=None, encoding='utf-8', default=None): indent=None, separators=None, encoding='utf-8', default=None):
""" """Constructor for JSONEncoder, with sensible defaults.
Constructor for JSONEncoder, with sensible defaults.
If skipkeys is False, then it is a TypeError to attempt If skipkeys is False, then it is a TypeError to attempt
encoding of keys that are not str, int, long, float or None. If encoding of keys that are not str, int, long, float or None. If
@ -158,6 +132,7 @@ class JSONEncoder(object):
If encoding is not None, then all input strings will be If encoding is not None, then all input strings will be
transformed into unicode using that encoding prior to JSON-encoding. transformed into unicode using that encoding prior to JSON-encoding.
The default is UTF-8. The default is UTF-8.
""" """
self.skipkeys = skipkeys self.skipkeys = skipkeys
@ -166,171 +141,20 @@ class JSONEncoder(object):
self.allow_nan = allow_nan self.allow_nan = allow_nan
self.sort_keys = sort_keys self.sort_keys = sort_keys
self.indent = indent self.indent = indent
self.current_indent_level = 0
if separators is not None: if separators is not None:
self.item_separator, self.key_separator = separators self.item_separator, self.key_separator = separators
if default is not None: if default is not None:
self.default = default self.default = default
self.encoding = encoding self.encoding = encoding
def _newline_indent(self):
return '\n' + (' ' * (self.indent * self.current_indent_level))
def _iterencode_list(self, lst, markers=None):
if not lst:
yield '[]'
return
if markers is not None:
markerid = id(lst)
if markerid in markers:
raise ValueError("Circular reference detected")
markers[markerid] = lst
yield '['
if self.indent is not None:
self.current_indent_level += 1
newline_indent = self._newline_indent()
separator = self.item_separator + newline_indent
yield newline_indent
else:
newline_indent = None
separator = self.item_separator
first = True
for value in lst:
if first:
first = False
else:
yield separator
for chunk in self._iterencode(value, markers):
yield chunk
if newline_indent is not None:
self.current_indent_level -= 1
yield self._newline_indent()
yield ']'
if markers is not None:
del markers[markerid]
def _iterencode_dict(self, dct, markers=None):
if not dct:
yield '{}'
return
if markers is not None:
markerid = id(dct)
if markerid in markers:
raise ValueError("Circular reference detected")
markers[markerid] = dct
yield '{'
key_separator = self.key_separator
if self.indent is not None:
self.current_indent_level += 1
newline_indent = self._newline_indent()
item_separator = self.item_separator + newline_indent
yield newline_indent
else:
newline_indent = None
item_separator = self.item_separator
first = True
if self.ensure_ascii:
encoder = encode_basestring_ascii
else:
encoder = encode_basestring
allow_nan = self.allow_nan
if self.sort_keys:
keys = dct.keys()
keys.sort()
items = [(k, dct[k]) for k in keys]
else:
items = dct.iteritems()
_encoding = self.encoding
_do_decode = (_encoding is not None
and not (_encoding == 'utf-8'))
for key, value in items:
if isinstance(key, str):
if _do_decode:
key = key.decode(_encoding)
elif isinstance(key, basestring):
pass
# JavaScript is weakly typed for these, so it makes sense to
# also allow them. Many encoders seem to do something like this.
elif isinstance(key, float):
key = floatstr(key, allow_nan)
elif isinstance(key, (int, long)):
key = str(key)
elif key is True:
key = 'true'
elif key is False:
key = 'false'
elif key is None:
key = 'null'
elif self.skipkeys:
continue
else:
raise TypeError("key %r is not a string" % (key,))
if first:
first = False
else:
yield item_separator
yield encoder(key)
yield key_separator
for chunk in self._iterencode(value, markers):
yield chunk
if newline_indent is not None:
self.current_indent_level -= 1
yield self._newline_indent()
yield '}'
if markers is not None:
del markers[markerid]
def _iterencode(self, o, markers=None):
if isinstance(o, basestring):
if self.ensure_ascii:
encoder = encode_basestring_ascii
else:
encoder = encode_basestring
_encoding = self.encoding
if (_encoding is not None and isinstance(o, str)
and not (_encoding == 'utf-8')):
o = o.decode(_encoding)
yield encoder(o)
elif o is None:
yield 'null'
elif o is True:
yield 'true'
elif o is False:
yield 'false'
elif isinstance(o, (int, long)):
yield str(o)
elif isinstance(o, float):
yield floatstr(o, self.allow_nan)
elif isinstance(o, (list, tuple)):
for chunk in self._iterencode_list(o, markers):
yield chunk
elif isinstance(o, dict):
for chunk in self._iterencode_dict(o, markers):
yield chunk
else:
if markers is not None:
markerid = id(o)
if markerid in markers:
raise ValueError("Circular reference detected")
markers[markerid] = o
for chunk in self._iterencode_default(o, markers):
yield chunk
if markers is not None:
del markers[markerid]
def _iterencode_default(self, o, markers=None):
newobj = self.default(o)
return self._iterencode(newobj, markers)
def default(self, o): def default(self, o):
""" """Implement this method in a subclass such that it returns
Implement this method in a subclass such that it returns
a serializable object for ``o``, or calls the base implementation a serializable object for ``o``, or calls the base implementation
(to raise a ``TypeError``). (to raise a ``TypeError``).
For example, to support arbitrary iterators, you could For example, to support arbitrary iterators, you could
implement default like this:: implement default like this::
def default(self, o): def default(self, o):
try: try:
iterable = iter(o) iterable = iter(o)
@ -339,21 +163,22 @@ class JSONEncoder(object):
else: else:
return list(iterable) return list(iterable)
return JSONEncoder.default(self, o) return JSONEncoder.default(self, o)
""" """
raise TypeError("%r is not JSON serializable" % (o,)) raise TypeError("%r is not JSON serializable" % (o,))
def encode(self, o): def encode(self, o):
""" """Return a JSON string representation of a Python data structure.
Return a JSON string representation of a Python data structure.
>>> JSONEncoder().encode({"foo": ["bar", "baz"]}) >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
'{"foo": ["bar", "baz"]}' '{"foo": ["bar", "baz"]}'
""" """
# This is for extremely simple cases and benchmarks. # This is for extremely simple cases and benchmarks.
if isinstance(o, basestring): if isinstance(o, basestring):
if isinstance(o, str): if isinstance(o, str):
_encoding = self.encoding _encoding = self.encoding
if (_encoding is not None if (_encoding is not None
and not (_encoding == 'utf-8')): and not (_encoding == 'utf-8')):
o = o.decode(_encoding) o = o.decode(_encoding)
if self.ensure_ascii: if self.ensure_ascii:
@ -363,23 +188,243 @@ class JSONEncoder(object):
# This doesn't pass the iterator directly to ''.join() because the # This doesn't pass the iterator directly to ''.join() because the
# exceptions aren't as detailed. The list call should be roughly # exceptions aren't as detailed. The list call should be roughly
# equivalent to the PySequence_Fast that ''.join() would do. # equivalent to the PySequence_Fast that ''.join() would do.
chunks = list(self.iterencode(o)) chunks = self.iterencode(o, _one_shot=True)
if not isinstance(chunks, (list, tuple)):
chunks = list(chunks)
return ''.join(chunks) return ''.join(chunks)
def iterencode(self, o): def iterencode(self, o, _one_shot=False):
""" """Encode the given object and yield each string
Encode the given object and yield each string
representation as available. representation as available.
For example:: For example::
for chunk in JSONEncoder().iterencode(bigobject): for chunk in JSONEncoder().iterencode(bigobject):
mysocket.write(chunk) mysocket.write(chunk)
""" """
if self.check_circular: if self.check_circular:
markers = {} markers = {}
else: else:
markers = None markers = None
return self._iterencode(o, markers) if self.ensure_ascii:
_encoder = encode_basestring_ascii
else:
_encoder = encode_basestring
if self.encoding != 'utf-8':
def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
if isinstance(o, str):
o = o.decode(_encoding)
return _orig_encoder(o)
__all__ = ['JSONEncoder'] def floatstr(o, allow_nan=self.allow_nan, _repr=FLOAT_REPR, _inf=INFINITY, _neginf=-INFINITY):
# Check for specials. Note that this type of test is processor- and/or
# platform-specific, so do tests which don't depend on the internals.
if o != o:
text = 'NaN'
elif o == _inf:
text = 'Infinity'
elif o == _neginf:
text = '-Infinity'
else:
return _repr(o)
if not allow_nan:
raise ValueError("Out of range float values are not JSON compliant: %r"
% (o,))
return text
if _one_shot and c_make_encoder is not None and not self.indent and not self.sort_keys:
_iterencode = c_make_encoder(
markers, self.default, _encoder, self.indent,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, self.allow_nan)
else:
_iterencode = _make_iterencode(
markers, self.default, _encoder, self.indent, floatstr,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, _one_shot)
return _iterencode(o, 0)
def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
## HACK: hand-optimized bytecode; turn globals into locals
False=False,
True=True,
ValueError=ValueError,
basestring=basestring,
dict=dict,
float=float,
id=id,
int=int,
isinstance=isinstance,
list=list,
long=long,
str=str,
tuple=tuple,
):
def _iterencode_list(lst, _current_indent_level):
if not lst:
yield '[]'
return
if markers is not None:
markerid = id(lst)
if markerid in markers:
raise ValueError("Circular reference detected")
markers[markerid] = lst
buf = '['
if _indent is not None:
_current_indent_level += 1
newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
separator = _item_separator + newline_indent
buf += newline_indent
else:
newline_indent = None
separator = _item_separator
first = True
for value in lst:
if first:
first = False
else:
buf = separator
if isinstance(value, basestring):
yield buf + _encoder(value)
elif value is None:
yield buf + 'null'
elif value is True:
yield buf + 'true'
elif value is False:
yield buf + 'false'
elif isinstance(value, (int, long)):
yield buf + str(value)
elif isinstance(value, float):
yield buf + _floatstr(value)
else:
yield buf
if isinstance(value, (list, tuple)):
chunks = _iterencode_list(value, _current_indent_level)
elif isinstance(value, dict):
chunks = _iterencode_dict(value, _current_indent_level)
else:
chunks = _iterencode(value, _current_indent_level)
for chunk in chunks:
yield chunk
if newline_indent is not None:
_current_indent_level -= 1
yield '\n' + (' ' * (_indent * _current_indent_level))
yield ']'
if markers is not None:
del markers[markerid]
def _iterencode_dict(dct, _current_indent_level):
if not dct:
yield '{}'
return
if markers is not None:
markerid = id(dct)
if markerid in markers:
raise ValueError("Circular reference detected")
markers[markerid] = dct
yield '{'
if _indent is not None:
_current_indent_level += 1
newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
item_separator = _item_separator + newline_indent
yield newline_indent
else:
newline_indent = None
item_separator = _item_separator
first = True
if _sort_keys:
items = dct.items()
items.sort(key=lambda kv: kv[0])
else:
items = dct.iteritems()
for key, value in items:
if isinstance(key, basestring):
pass
# JavaScript is weakly typed for these, so it makes sense to
# also allow them. Many encoders seem to do something like this.
elif isinstance(key, float):
key = _floatstr(key)
elif isinstance(key, (int, long)):
key = str(key)
elif key is True:
key = 'true'
elif key is False:
key = 'false'
elif key is None:
key = 'null'
elif _skipkeys:
continue
else:
raise TypeError("key %r is not a string" % (key,))
if first:
first = False
else:
yield item_separator
yield _encoder(key)
yield _key_separator
if isinstance(value, basestring):
yield _encoder(value)
elif value is None:
yield 'null'
elif value is True:
yield 'true'
elif value is False:
yield 'false'
elif isinstance(value, (int, long)):
yield str(value)
elif isinstance(value, float):
yield _floatstr(value)
else:
if isinstance(value, (list, tuple)):
chunks = _iterencode_list(value, _current_indent_level)
elif isinstance(value, dict):
chunks = _iterencode_dict(value, _current_indent_level)
else:
chunks = _iterencode(value, _current_indent_level)
for chunk in chunks:
yield chunk
if newline_indent is not None:
_current_indent_level -= 1
yield '\n' + (' ' * (_indent * _current_indent_level))
yield '}'
if markers is not None:
del markers[markerid]
def _iterencode(o, _current_indent_level):
if isinstance(o, basestring):
yield _encoder(o)
elif o is None:
yield 'null'
elif o is True:
yield 'true'
elif o is False:
yield 'false'
elif isinstance(o, (int, long)):
yield str(o)
elif isinstance(o, float):
yield _floatstr(o)
elif isinstance(o, (list, tuple)):
for chunk in _iterencode_list(o, _current_indent_level):
yield chunk
elif isinstance(o, dict):
for chunk in _iterencode_dict(o, _current_indent_level):
yield chunk
else:
if markers is not None:
markerid = id(o)
if markerid in markers:
raise ValueError("Circular reference detected")
markers[markerid] = o
o = _default(o)
for chunk in _iterencode(o, _current_indent_level):
yield chunk
if markers is not None:
del markers[markerid]
return _iterencode

View File

@ -1,67 +1,65 @@
""" """JSON token scanner
Iterator based sre token scanner
""" """
import re import re
from re import VERBOSE, MULTILINE, DOTALL try:
import sre_parse from simplejson._speedups import make_scanner as c_make_scanner
import sre_compile except ImportError:
import sre_constants c_make_scanner = None
from sre_constants import BRANCH, SUBPATTERN
__all__ = ['Scanner', 'pattern'] __all__ = ['make_scanner']
FLAGS = (VERBOSE | MULTILINE | DOTALL) NUMBER_RE = re.compile(
r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
(re.VERBOSE | re.MULTILINE | re.DOTALL))
class Scanner(object): def py_make_scanner(context):
def __init__(self, lexicon, flags=FLAGS): parse_object = context.parse_object
self.actions = [None] parse_array = context.parse_array
# Combine phrases into a compound pattern parse_string = context.parse_string
s = sre_parse.Pattern() match_number = NUMBER_RE.match
s.flags = flags encoding = context.encoding
p = [] strict = context.strict
for idx, token in enumerate(lexicon): parse_float = context.parse_float
phrase = token.pattern parse_int = context.parse_int
try: parse_constant = context.parse_constant
subpattern = sre_parse.SubPattern(s, object_hook = context.object_hook
[(SUBPATTERN, (idx + 1, sre_parse.parse(phrase, flags)))])
except sre_constants.error:
raise
p.append(subpattern)
self.actions.append(token)
s.groups = len(p) + 1 # NOTE(guido): Added to make SRE validation work def _scan_once(string, idx):
p = sre_parse.SubPattern(s, [(BRANCH, (None, p))]) try:
self.scanner = sre_compile.compile(p) nextchar = string[idx]
except IndexError:
raise StopIteration
def iterscan(self, string, idx=0, context=None): if nextchar == '"':
""" return parse_string(string, idx + 1, encoding, strict)
Yield match, end_idx for each match elif nextchar == '{':
""" return parse_object((string, idx + 1), encoding, strict, _scan_once, object_hook)
match = self.scanner.scanner(string, idx).match elif nextchar == '[':
actions = self.actions return parse_array((string, idx + 1), _scan_once)
lastend = idx elif nextchar == 'n' and string[idx:idx + 4] == 'null':
end = len(string) return None, idx + 4
while True: elif nextchar == 't' and string[idx:idx + 4] == 'true':
m = match() return True, idx + 4
if m is None: elif nextchar == 'f' and string[idx:idx + 5] == 'false':
break return False, idx + 5
matchbegin, matchend = m.span()
if lastend == matchend:
break
action = actions[m.lastindex]
if action is not None:
rval, next_pos = action(m, context)
if next_pos is not None and next_pos != matchend:
# "fast forward" the scanner
matchend = next_pos
match = self.scanner.scanner(string, matchend).match
yield rval, matchend
lastend = matchend
m = match_number(string, idx)
if m is not None:
integer, frac, exp = m.groups()
if frac or exp:
res = parse_float(integer + (frac or '') + (exp or ''))
else:
res = parse_int(integer)
return res, m.end()
elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
return parse_constant('NaN'), idx + 3
elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
return parse_constant('Infinity'), idx + 8
elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
return parse_constant('-Infinity'), idx + 9
else:
raise StopIteration
def pattern(pattern, flags=FLAGS): return _scan_once
def decorator(fn):
fn.pattern = pattern make_scanner = c_make_scanner or py_make_scanner
fn.regex = re.compile(pattern, flags)
return fn
return decorator

View File

@ -1,23 +1,14 @@
r""" r"""Using simplejson from the shell to validate and
Using simplejson from the shell to validate and
pretty-print:: pretty-print::
$ echo '{"json":"obj"}' | python -msimplejson $ echo '{"json":"obj"}' | python -msimplejson.tool
{ {
"json": "obj" "json": "obj"
} }
$ echo '{ 1.2:3.4}' | python -msimplejson $ echo '{ 1.2:3.4}' | python -msimplejson.tool
Expecting property name: line 1 column 2 (char 2) Expecting property name: line 1 column 2 (char 2)
Note that the JSON produced by this module's default settings
is a subset of YAML, so it may be used as a serializer for that as well.
""" """
import django.utils.simplejson from django.utils import simplejson
#
# Pretty printer:
# curl http://mochikit.com/examples/ajax_tables/domains.json | python -msimplejson.tool
#
def main(): def main():
import sys import sys

View File

@ -162,11 +162,17 @@ For example::
json_serializer = serializers.get_serializer("json")() json_serializer = serializers.get_serializer("json")()
json_serializer.serialize(queryset, ensure_ascii=False, stream=response) json_serializer.serialize(queryset, ensure_ascii=False, stream=response)
The Django source code includes the simplejson_ module. Be aware that if you're The Django source code includes the simplejson_ module. However, if you're
serializing using that module directly, not all Django output can be passed using Python 2.6 (which includes a builtin version of the module), Django will
unmodified to simplejson. In particular, :ref:`lazy translation objects use the builtin ``json`` module automatically. If you have a system installed
<lazy-translations>` need a `special encoder`_ written for them. Something like version that includes the C-based speedup extension, or your system version is
this will work:: more recent than the version shipped with Django (currently, 2.0.7), the
system version will be used instead of the version included with Django.
Be aware that if you're serializing using that module directly, not all Django
output can be passed unmodified to simplejson. In particular, :ref:`lazy
translation objects <lazy-translations>` need a `special encoder`_ written for
them. Something like this will work::
from django.utils.functional import Promise from django.utils.functional import Promise
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
@ -178,3 +184,4 @@ this will work::
return obj return obj
.. _special encoder: http://svn.red-bean.com/bob/simplejson/tags/simplejson-1.7/docs/index.html .. _special encoder: http://svn.red-bean.com/bob/simplejson/tags/simplejson-1.7/docs/index.html