1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
.. -*- coding: utf-8 -*-
.. :Project: python-rapidjson -- Decoder class documentation
.. :Author: Lele Gaifax <lele@metapensiero.it>
.. :License: MIT License
.. :Copyright: © 2017, 2018, 2020, 2021 Lele Gaifax
..
===============
Decoder class
===============
.. currentmodule:: rapidjson
.. testsetup::
import io
from rapidjson import Decoder, Encoder, DM_ISO8601
.. class:: Decoder(number_mode=None, datetime_mode=None, uuid_mode=None, parse_mode=None)
Class-based :func:`loads`\ -like functionality.
:param int number_mode: enable particular :ref:`behaviors in handling numbers
<loads-number-mode>`
:param int datetime_mode: how should :ref:`datetime, time and date instances be handled
<loads-datetime-mode>`
:param int uuid_mode: how should :ref:`UUID instances be handled <loads-uuid-mode>`
:param int parse_mode: whether the parser should allow :ref:`non-standard JSON
extensions <loads-parse-mode>`
.. rubric:: Attributes
.. attribute:: datetime_mode
:type: int
The datetime mode, whether and how datetime literals will be recognized.
.. attribute:: number_mode
:type: int
The number mode, whether numeric literals will be decoded.
.. attribute:: parse_mode
:type: int
The parse mode, whether comments and trailing commas are allowed.
.. attribute:: uuid_mode
:type: int
The UUID mode, whether and how UUID literals will be recognized.
.. rubric:: Methods
.. method:: __call__(json, *, chunk_size=65536)
:param json: either a ``str`` instance, an *UTF-8* ``bytes`` instance or a
*file-like* stream, containing the ``JSON`` to be decoded
:param int chunk_size: in case of a stream, it will be read in chunks of this size
:returns: a Python value
.. doctest::
>>> decoder = Decoder()
>>> decoder('"€ 0.50"')
'€ 0.50'
>>> decoder(io.StringIO('"€ 0.50"'))
'€ 0.50'
>>> decoder(io.BytesIO(b'"\xe2\x82\xac 0.50"'))
'€ 0.50'
>>> decoder(b'"\xe2\x82\xac 0.50"')
'€ 0.50'
.. method:: end_array(sequence)
:param sequence: an instance implement the *mutable sequence* protocol
:returns: a new value
This is called, if implemented, when a *JSON array* has been completely parsed, and
can be used replace it with an arbitrary different value:
.. doctest::
>>> class TupleDecoder(Decoder):
... def end_array(self, a):
... return tuple(a)
...
>>> td = TupleDecoder()
>>> res = td('[{"one": [1]}, {"two":[2,3]}]')
>>> isinstance(res, tuple)
True
>>> res[0]
{'one': (1,)}
>>> res[1]
{'two': (2, 3)}
.. method:: end_object(mapping)
:param mapping: an instance representing the JSON object, either one implementing
the *mapping protocol* or a list containing ``(key, value)`` tuples
:returns: a new value
This is called, if implemented, when a *JSON object* has been completely parsed, and
can be used to replace it with an arbitrary different value, like what can be done
with the ``object_hook`` argument of the :func:`loads` function:
.. doctest::
>>> class Point(object):
... def __init__(self, x, y):
... self.x = x
... self.y = y
... def __repr__(self):
... return 'Point(%s, %s)' % (self.x, self.y)
...
>>> class PointDecoder(Decoder):
... def end_object(self, d):
... if 'x' in d and 'y' in d:
... return Point(d['x'], d['y'])
... else:
... return d
...
>>> pd = PointDecoder()
>>> pd('{"x":1,"y":2}')
Point(1, 2)
When :meth:`start_object` returns a ``list`` instance, then the `mapping` argument
is actually a list of tuples.
.. method:: start_object()
:returns: either a list or mapping instance
This method, when implemented, is called whenever a new *JSON object* is found: it
must return either a list or an instance implementing the *mapping protocol*.
It can be used to select a different implementation than the standard ``dict`` used
by default:
.. doctest::
>>> from collections import OrderedDict
>>> class OrderedDecoder(Decoder):
... def start_object(self):
... return OrderedDict()
...
>>> od = OrderedDecoder()
>>> type(od('{"foo": "bar"}'))
<class 'collections.OrderedDict'>
By returning a ``list`` you obtain a different handling of *JSON objects*: instead
of translating them into Python maps as soon as they are found, their *key-value
tuples* are accumulated in the given list; when the *JSON object* has been
completely parsed, then the sequence is passed to the method :meth:`end_object`, if
implemented, that will reasonably transmogrify it into some kind of dictionary. When
that method is missing, then the list is kept as is, thus representing all objects
in the JSON origin as lists of key-value tuples in Python:
.. doctest::
>>> class KVPairsDecoder(Decoder):
... def start_object(self):
... return []
...
>>> kvpd = KVPairsDecoder()
>>> kvpd('{"one": 1, "two": 2}')
[('one', 1), ('two', 2)]
.. method:: string(s)
:param s: a ``str`` instance
:returns: a new value
This method, when implemented, is called whenever a *JSON string* has been
completely parsed, and can be used to replace it with an arbitrary different value:
.. doctest::
>>> class SwapStringCase(Decoder):
... def string(self, s):
... return s.swapcase()
...
>>> ssc = SwapStringCase()
>>> ssc('"Hello World!"')
'hELLO wORLD!'
Note that it is called **after** the recognition of dates and UUIDs, when
`datetime_mode` and/or `uuid_mode` are specified:
.. doctest::
>>> class DDMMYYYY(Decoder):
... def string(self, s):
... if len(s) == 8 and s.isdigit():
... dd = int(s[:2])
... mm = int(s[2:4])
... yyyy = int(s[-4:])
... return (yyyy, mm, dd)
... return s
...
>>> ddmmyyyy = DDMMYYYY(datetime_mode=DM_ISO8601)
>>> ddmmyyyy('["2017-08-21", "21082017"]')
[datetime.date(2017, 8, 21), (2017, 8, 21)]
|