Skip to content
This repository was archived by the owner on Aug 19, 2025. It is now read-only.

Commit 94b0fe1

Browse files
shamrinlovelydinosaur
authored andcommitted
Array serialization (#77)
* Array serialization * support positional Array; make mypy happy * support Array(items=None)
1 parent 0e50403 commit 94b0fe1

2 files changed

Lines changed: 111 additions & 0 deletions

File tree

tests/test_schemas.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,52 @@ def test_schema_serialization():
129129
assert data == {"name": "T-Shirt", "rating": None}
130130

131131

132+
def test_schema_null_items_array_serialization():
133+
class Product(typesystem.Schema):
134+
names = typesystem.Array()
135+
136+
tshirt = Product(names=[1, "2", {"nested": 3}])
137+
138+
data = dict(tshirt)
139+
140+
assert data == {"names": [1, "2", {"nested": 3}]}
141+
142+
143+
def test_schema_string_array_serialization():
144+
class Product(typesystem.Schema):
145+
names = typesystem.Array(typesystem.String())
146+
147+
tshirt = Product(names=["T-Shirt"])
148+
149+
data = dict(tshirt)
150+
151+
assert data == {"names": ["T-Shirt"]}
152+
153+
154+
def test_schema_dates_array_serialization():
155+
class BlogPost(typesystem.Schema):
156+
text = typesystem.String()
157+
modified = typesystem.Array(typesystem.Date())
158+
159+
post = BlogPost(text="Hi", modified=[datetime.date.today()])
160+
161+
data = dict(post)
162+
163+
assert data["text"] == "Hi"
164+
assert data["modified"] == [datetime.date.today().isoformat()]
165+
166+
167+
def test_schema_positional_array_serialization():
168+
class NumberName(typesystem.Schema):
169+
pair = typesystem.Array([typesystem.Integer(), typesystem.String()])
170+
171+
name = NumberName(pair=[1, "one"])
172+
173+
data = dict(name)
174+
175+
assert data == {"pair": [1, "one"]}
176+
177+
132178
def test_schema_len():
133179
tshirt = Product(name="T-Shirt")
134180

@@ -280,6 +326,56 @@ class Album(typesystem.Schema):
280326
}
281327

282328

329+
def test_nested_schema_array():
330+
class Artist(typesystem.Schema):
331+
name = typesystem.String(max_length=100)
332+
333+
class Album(typesystem.Schema):
334+
title = typesystem.String(max_length=100)
335+
release_year = typesystem.Integer()
336+
artists = typesystem.Array(items=typesystem.Reference(Artist))
337+
338+
value = Album.validate(
339+
{
340+
"title": "Double Negative",
341+
"release_year": "2018",
342+
"artists": [{"name": "Low"}],
343+
}
344+
)
345+
assert dict(value) == {
346+
"title": "Double Negative",
347+
"release_year": 2018,
348+
"artists": [{"name": "Low"}],
349+
}
350+
assert value == Album(
351+
title="Double Negative", release_year=2018, artists=[Artist(name="Low")]
352+
)
353+
354+
value, error = Album.validate_or_error(
355+
{"title": "Double Negative", "release_year": "2018", "artists": None}
356+
)
357+
assert dict(error) == {"artists": "May not be null."}
358+
359+
value, error = Album.validate_or_error(
360+
{"title": "Double Negative", "release_year": "2018", "artists": "Low"}
361+
)
362+
assert dict(error) == {"artists": "Must be an array."}
363+
364+
class Album(typesystem.Schema):
365+
title = typesystem.String(max_length=100)
366+
release_year = typesystem.Integer()
367+
artists = typesystem.Array(items=typesystem.Reference(Artist), allow_null=True)
368+
369+
value = Album.validate(
370+
{"title": "Double Negative", "release_year": "2018", "artists": None}
371+
)
372+
assert dict(value) == {
373+
"title": "Double Negative",
374+
"release_year": 2018,
375+
"artists": None,
376+
}
377+
378+
283379
def test_nested_schema_to_json_schema():
284380
class Artist(typesystem.Schema):
285381
name = typesystem.String(max_length=100)

typesystem/fields.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,21 @@ def validate(self, value: typing.Any, *, strict: bool = False) -> typing.Any:
658658

659659
return validated
660660

661+
def serialize(self, obj: typing.Any) -> typing.Any:
662+
if obj is None:
663+
return None
664+
665+
if isinstance(self.items, list):
666+
return [
667+
serializer.serialize(value)
668+
for serializer, value in zip(self.items, obj)
669+
]
670+
671+
if self.items is None:
672+
return obj
673+
674+
return [self.items.serialize(value) for value in obj]
675+
661676

662677
class Text(String):
663678
def __init__(self, **kwargs: typing.Any) -> None:

0 commit comments

Comments
 (0)