File: decoder.rst

package info (click to toggle)
python-rapidjson 1.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 7,368 kB
  • sloc: cpp: 3,332; python: 1,990; makefile: 106
file content (207 lines) | stat: -rw-r--r-- 6,766 bytes parent folder | download | duplicates (2)
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)]