Skip to content

Commit 260fd4a

Browse files
committed
Serialization docs
1 parent 88abb3e commit 260fd4a

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Contents
6868
http
6969
certificates
7070
compression
71+
serialization
7172
errors
7273
errno
7374
logging

docs/serialization.rst

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
Serialization
2+
-------------
3+
4+
There are two serialization mechanisms employed by the driver:
5+
6+
* JSON serialization/deserialization
7+
* Document serialization/deserialization
8+
9+
All serializers must inherit from the :class:`arangoasync.serialization.Serializer` class. They must
10+
implement a :func:`arangoasync.serialization.Serializer.dumps` method can handle both
11+
single objects and sequences.
12+
13+
Deserializers mush inherit from the :class:`arangoasync.serialization.Deserializer` class. These have
14+
two methods, :func:`arangoasync.serialization.Deserializer.loads` and :func:`arangoasync.serialization.Deserializer.loads_many`,
15+
which must handle loading of a single document and multiple documents, respectively.
16+
17+
JSON
18+
====
19+
20+
Usually there's no need to implement your own JSON serializer/deserializer, but such an
21+
implementation could look like the following.
22+
23+
**Example:**
24+
25+
.. code-block:: python
26+
27+
import json
28+
from typing import Sequence, cast
29+
from arangoasync.collection import StandardCollection
30+
from arangoasync.database import StandardDatabase
31+
from arangoasync.exceptions import DeserializationError, SerializationError
32+
from arangoasync.serialization import Serializer, Deserializer
33+
from arangoasync.typings import Json, Jsons
34+
35+
36+
class CustomJsonSerializer(Serializer[Json]):
37+
def dumps(self, data: Json | Sequence[str | Json]) -> str:
38+
try:
39+
return json.dumps(data, separators=(",", ":"))
40+
except Exception as e:
41+
raise SerializationError("Failed to serialize data to JSON.") from e
42+
43+
44+
class CustomJsonDeserializer(Deserializer[Json, Jsons]):
45+
def loads(self, data: bytes) -> Json:
46+
try:
47+
return json.loads(data) # type: ignore[no-any-return]
48+
except Exception as e:
49+
raise DeserializationError("Failed to deserialize data from JSON.") from e
50+
51+
def loads_many(self, data: bytes) -> Jsons:
52+
return self.loads(data) # type: ignore[return-value]
53+
54+
You would then use the custom serializer/deserializer when creating a client:
55+
56+
.. code-block:: python
57+
58+
from arangoasync import ArangoClient
59+
from arangoasync.auth import Auth
60+
61+
# Initialize the client for ArangoDB.
62+
async with ArangoClient(
63+
hosts="http://localhost:8529",
64+
serializer=CustomJsonSerializer(),
65+
deserializer=CustomJsonDeserializer(),
66+
) as client:
67+
auth = Auth(username="root", password="passwd")
68+
69+
# Connect to "test" database as root user.
70+
test = await client.db("test", auth=auth)
71+
72+
Documents
73+
=========
74+
75+
By default, the JSON serializer/deserializer is used for documents too, but you can provide your own
76+
document serializer and deserializer for fine-grained control over the format of a collection. Say
77+
that you are modeling your students data using Pydantic_. You want to be able to insert documents
78+
of a certain type, and also be able to read them back. More so, you would like to get multiple documents
79+
back using one of the formats provided by pandas_.
80+
81+
**Example:**
82+
83+
.. code-block:: python
84+
85+
import json
86+
import pandas as pd
87+
import pydantic
88+
import pydantic_core
89+
from typing import Sequence, cast
90+
from arangoasync import ArangoClient
91+
from arangoasync.auth import Auth
92+
from arangoasync.collection import StandardCollection
93+
from arangoasync.database import StandardDatabase
94+
from arangoasync.exceptions import DeserializationError, SerializationError
95+
from arangoasync.serialization import Serializer, Deserializer
96+
from arangoasync.typings import Json, Jsons
97+
98+
99+
class Student(pydantic.BaseModel):
100+
name: str
101+
age: int
102+
103+
104+
class StudentSerializer(Serializer[Student]):
105+
def dumps(self, data: Student | Sequence[Student | str]) -> str:
106+
try:
107+
if isinstance(data, Student):
108+
return data.model_dump_json()
109+
else:
110+
# You are required to support both str and Student types.
111+
serialized_data = []
112+
for student in data:
113+
if isinstance(student, str):
114+
serialized_data.append(student)
115+
else:
116+
serialized_data.append(student.model_dump())
117+
return json.dumps(serialized_data, separators=(",", ":"))
118+
except Exception as e:
119+
raise SerializationError("Failed to serialize data.") from e
120+
121+
122+
class StudentDeserializer(Deserializer[Student, pd.DataFrame]):
123+
def loads(self, data: bytes) -> Student:
124+
# Load a single document.
125+
try:
126+
return Student.model_validate(pydantic_core.from_json(data))
127+
except Exception as e:
128+
raise DeserializationError("Failed to deserialize data.") from e
129+
130+
def loads_many(self, data: bytes) -> pd.DataFrame:
131+
# Load multiple documents.
132+
return pd.DataFrame(json.loads(data))
133+
134+
You would then use the custom serializer/deserializer when working with collections:
135+
136+
**Example:**
137+
138+
.. code-block:: python
139+
140+
async def main():
141+
# Initialize the client for ArangoDB.
142+
async with ArangoClient(
143+
hosts="http://localhost:8529",
144+
serializer=CustomJsonSerializer(),
145+
deserializer=CustomJsonDeserializer(),
146+
) as client:
147+
auth = Auth(username="root", password="passwd")
148+
149+
# Connect to "test" database as root user.
150+
db: StandardDatabase = await client.db("test", auth=auth, verify=True)
151+
152+
# Populate the "students" collection.
153+
col = cast(
154+
StandardCollection[Student, Student, pd.DataFrame],
155+
db.collection(
156+
"students",
157+
doc_serializer=StudentSerializer(),
158+
doc_deserializer=StudentDeserializer()),
159+
)
160+
161+
# Insert one document.
162+
doc = cast(Json, await col.insert(Student(name="John Doe", age=20)))
163+
164+
# Insert multiple documents.
165+
docs = cast(Jsons, await col.insert_many([
166+
Student(name="Jane Doe", age=22),
167+
Student(name="Alice Smith", age=19),
168+
Student(name="Bob Johnson", age=21),
169+
]))
170+
171+
# Get one document.
172+
john = await col.get(doc)
173+
assert type(john) == Student
174+
175+
# Get multiple documents.
176+
keys = [doc["_key"] for doc in docs]
177+
students = await col.get_many(keys)
178+
assert type(students) == pd.DataFrame
179+
180+
.. _Pydantic: https://docs.pydantic.dev/latest/
181+
.. _pandas: https://pandas.pydata.org/

0 commit comments

Comments
 (0)