Skip to content

Commit 42ab948

Browse files
authored
doc: add JSON interop and optional field docs (#69)
Add documentation for JSON serialization/deserialization Add documentation for making optional fields and determining field presence.
1 parent 2982ab5 commit 42ab948

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

packages/proto-plus/docs/fields.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,51 @@ a string (which should match for all fields within the oneof):
120120
implementation of protocol buffers will reject the message. They need not
121121
have consecutive field numbers, but they must be declared in consecutive
122122
order.
123+
124+
125+
Optional fields
126+
---------------
127+
128+
All fields in protocol buffers are optional, but it is often necessary to
129+
check for field presence. Sometimes legitimate values for fields can be falsy,
130+
so checking for truthiness is not sufficient. Proto3 v3.12.0 added the
131+
``optional`` keyword to field descriptions, which enables a mechanism for
132+
checking field presence.
133+
134+
In proto plus, fields can be marked as optional by passing ``optional=True``
135+
in the constructor. The message *class* then gains a field of the same name
136+
that can be used to detect whether the field is present in message *instances*.
137+
138+
.. code-block:: python
139+
140+
class Song(proto.Message):
141+
composer = proto.Field(Composer, number=1)
142+
title = proto.Field(proto.STRING, number=2)
143+
lyrics = proto.Field(proto.STRING, number=3)
144+
year = proto.Field(proto.INT32, number=4)
145+
performer = proto.Field(proto.STRING, number=5, optional=True)
146+
147+
>>> s = Song(
148+
... composer={'given_name': 'Johann', 'family_name': 'Pachelbel'},
149+
... title='Canon in D',
150+
... year=1680,
151+
... genre=Genre.CLASSICAL,
152+
... )
153+
>>> Song.performer in s
154+
False
155+
>>> s.performer = 'Brahms'
156+
>>> Song.performer in s
157+
True
158+
>>> del s.performer
159+
>>> Song.performer in s
160+
False
161+
>>> s.performer = "" # The mysterious, unnamed composer
162+
>>> Song.performer in s
163+
True
164+
165+
166+
Under the hood, fields marked as optional are implemented via a synthetic
167+
one-variant ``oneof``. See the protocolbuffers documentation_ for more
168+
information.
169+
170+
.. _documentation: https://github.com/protocolbuffers/protobuf/blob/v3.12.0/docs/field_presence.md

packages/proto-plus/docs/messages.rst

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ A few things to note:
5050

5151
* This library only handles proto3.
5252
* The ``number`` is really a field ID. It is *not* a value of any kind.
53-
* All fields are optional (as is always the case in proto3). As a general
54-
rule, there is no distinction between setting the type's falsy value and
55-
not setting it at all (although there are exceptions to this in some cases).
53+
* All fields are optional (as is always the case in proto3).
54+
The only general way to determine whether a field was explicitly set to its
55+
falsy value or not set all is to mark it ``optional``.
56+
* Because all fields are optional, it is the responsibility of application logic
57+
to determine whether a necessary field has been set.
5658

5759
.. _messages: https://developers.google.com/protocol-buffers/docs/proto3#simple
5860

@@ -153,3 +155,13 @@ returns an instance of the message:
153155
.. code-block:: python
154156
155157
song = Song.deserialize(serialized_song)
158+
159+
JSON serialization and deserialization are also available from message *classes*
160+
via the :meth:`~.Message.to_json` and :meth:`~.Message.from_json` methods.
161+
162+
.. code-block:: python
163+
164+
json = Song.to_json(song)
165+
166+
new_song = Song.from_json(json)
167+

0 commit comments

Comments
 (0)