Skip to content

Commit

Permalink
feat: format specific (de)serialize (#25)
Browse files Browse the repository at this point in the history
Added functionality to implement custom (de)serialization specific for XML or JSON ([#13](#13)).


Changed
-------------
* Class `BaseHelper` is no longer abstract.  
  This class does not provide any functionality, it is more like a Protocol with some fallback implementations.
* Method `BaseHelper.serialize()` is no longer abstract.
  Will raise `NotImplementedError` per default. 
* Method `BaseHelper.deserialize()` is no longer abstract.
  Will raise `NotImplementedError` per default. 

Added
----------
* New method `BaseHelper.json_serialize()` predefined.  
  Will call `cls.serialize()` per default. 
* New method `BaseHelper.json_deserialize()` predefined.  
  Will call `cls.deserialize()` per default. 



----


Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
  • Loading branch information
jkowalleck authored Oct 1, 2023
1 parent 9a2798d commit dc998df
Show file tree
Hide file tree
Showing 20 changed files with 90 additions and 34 deletions.
14 changes: 7 additions & 7 deletions serializable/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def default(self, o: Any) -> Any:

if prop_info.custom_type:
if prop_info.is_helper_type():
v = prop_info.custom_type.serialize(v)
v = prop_info.custom_type.json_serialize(v)
else:
v = prop_info.custom_type(v)
elif prop_info.is_array:
Expand Down Expand Up @@ -285,7 +285,7 @@ def _from_json(cls: Type[_T], data: Dict[str, Any]) -> object:
try:
if prop_info.custom_type:
if prop_info.is_helper_type():
_data[k] = prop_info.custom_type.deserialize(v)
_data[k] = prop_info.custom_type.json_deserialize(v)
else:
_data[k] = prop_info.custom_type(v)
elif prop_info.is_array:
Expand Down Expand Up @@ -348,7 +348,7 @@ def _as_xml(self: _T, view_: Optional[Type[_T]] = None, as_string: bool = True,
new_key = CurrentFormatter.formatter.encode(property_name=new_key)

if prop_info.custom_type and prop_info.is_helper_type():
v = prop_info.custom_type.serialize(v)
v = prop_info.custom_type.xml_serialize(v)
elif prop_info.is_enum:
v = v.value

Expand Down Expand Up @@ -412,7 +412,7 @@ def _as_xml(self: _T, view_: Optional[Type[_T]] = None, as_string: bool = True,
SubElement(nested_e, nested_key).text = str(j)
elif prop_info.custom_type:
if prop_info.is_helper_type():
SubElement(this_e, new_key).text = str(prop_info.custom_type.serialize(v))
SubElement(this_e, new_key).text = str(prop_info.custom_type.xml_serialize(v))
else:
SubElement(this_e, new_key).text = str(prop_info.custom_type(v))
elif prop_info.is_enum:
Expand Down Expand Up @@ -488,7 +488,7 @@ def strip_default_namespace(s: str) -> str:
f'{cls.__module__}.{cls.__qualname__} which has Prop Metadata: {prop_info}')

if prop_info.custom_type and prop_info.is_helper_type():
_data[decoded_k] = prop_info.custom_type.deserialize(v)
_data[decoded_k] = prop_info.custom_type.xml_deserialize(v)
elif prop_info.is_enum:
_data[decoded_k] = prop_info.concrete_type(v)
elif prop_info.is_primitive_type():
Expand Down Expand Up @@ -553,14 +553,14 @@ def strip_default_namespace(s: str) -> str:
)
elif prop_info.custom_type:
if prop_info.is_helper_type():
_data[decoded_k] = prop_info.custom_type.deserialize(child_e)
_data[decoded_k] = prop_info.custom_type.xml_deserialize(child_e)
else:
_data[decoded_k] = prop_info.custom_type(child_e.text)
else:
_data[decoded_k].append(prop_info.concrete_type(child_e.text))
elif prop_info.custom_type:
if prop_info.is_helper_type():
_data[decoded_k] = prop_info.custom_type.deserialize(child_e.text)
_data[decoded_k] = prop_info.custom_type.xml_deserialize(child_e.text)
else:
_data[decoded_k] = prop_info.custom_type(child_e.text)
elif prop_info.is_enum:
Expand Down
55 changes: 45 additions & 10 deletions serializable/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,60 @@
# Copyright (c) Paul Horton. All Rights Reserved.
import re
import warnings
from abc import ABC, abstractmethod
from datetime import date, datetime
from typing import Any


class BaseHelper(ABC):
class BaseHelper:
"""Base Helper.
def __init__(self, *args: Any, **kwargs: Any) -> None:
pass
Inherit from this class and implement the needed functions!
This class does not provide any functionality,
it is more like a Protocol with some fallback implementations.
"""

# region general/fallback

@classmethod
def serialize(cls, o: Any) -> Any:
"""general purpose serializer"""
raise NotImplementedError()

@classmethod
def deserialize(cls, o: Any) -> Any:
"""general purpose deserializer"""
raise NotImplementedError()

# endregion general/fallback

# region json specific

@classmethod
@abstractmethod
def serialize(cls, o: object) -> Any:
raise NotImplementedError
def json_serialize(cls, o: Any) -> Any:
"""json specific serializer"""
return cls.serialize(o)

@classmethod
@abstractmethod
def deserialize(cls, o: str) -> Any:
raise NotImplementedError
def json_deserialize(cls, o: Any) -> Any:
"""json specific deserializer"""
return cls.deserialize(o)

# endregion json specific

# endregion xml specific

@classmethod
def xml_serialize(cls, o: Any) -> Any:
"""xml specific serializer"""
return cls.serialize(o)

@classmethod
def xml_deserialize(cls, o: Any) -> Any:
"""xml specific deserializer"""
return cls.deserialize(o)

# endregion xml specific


class Iso8601Date(BaseHelper):
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-camel-case-1-v1.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<book isbnNumber="978-1942788294">
<id>f3758bf0-0ff7-4366-a5e5-c209d4352b2d</id>
<title>The Phoenix Project</title>
<title>{X} The Phoenix Project</title>
<edition number="5">5th Anniversary Limited Edition</edition>
<publishDate>2018-04-16</publishDate>
<author>Kevin Behr</author>
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-camel-case-1-v2.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<book isbnNumber="978-1942788294">
<id>f3758bf0-0ff7-4366-a5e5-c209d4352b2d</id>
<title>The Phoenix Project</title>
<title>{X} The Phoenix Project</title>
<edition number="5">5th Anniversary Limited Edition</edition>
<publishDate>2018-04-16</publishDate>
<author>Kevin Behr</author>
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-camel-case-1-v3.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<book isbnNumber="978-1942788294">
<id>f3758bf0-0ff7-4366-a5e5-c209d4352b2d</id>
<title>The Phoenix Project</title>
<title>{X} The Phoenix Project</title>
<edition number="5">5th Anniversary Limited Edition</edition>
<publishDate>2018-04-16</publishDate>
<author>Kevin Behr</author>
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-camel-case-1-v4.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<book isbnNumber="978-1942788294">
<id>f3758bf0-0ff7-4366-a5e5-c209d4352b2d</id>
<title>The Phoenix Project</title>
<title>{X} The Phoenix Project</title>
<edition number="5">5th Anniversary Limited Edition</edition>
<publishDate>2018-04-16</publishDate>
<author>Kevin Behr</author>
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-camel-case-1.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<book isbnNumber="978-1942788294">
<id>f3758bf0-0ff7-4366-a5e5-c209d4352b2d</id>
<title>The Phoenix Project</title>
<title>{X} The Phoenix Project</title>
<edition number="5">5th Anniversary Limited Edition</edition>
<publishDate>2018-04-16</publishDate>
<author>Kevin Behr</author>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "f3758bf0-0ff7-4366-a5e5-c209d4352b2d",
"title": "The Phoenix Project",
"title": "{J} The Phoenix Project",
"isbnNumber": "978-1942788294",
"edition": {
"number": 5,
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-camel-case-v2.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "f3758bf0-0ff7-4366-a5e5-c209d4352b2d",
"title": "The Phoenix Project",
"title": "{J} The Phoenix Project",
"isbnNumber": "978-1942788294",
"edition": {
"number": 5,
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-camel-case-v3.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "f3758bf0-0ff7-4366-a5e5-c209d4352b2d",
"title": "The Phoenix Project",
"title": "{J} The Phoenix Project",
"isbnNumber": "978-1942788294",
"edition": {
"number": 5,
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-camel-case-v4.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "f3758bf0-0ff7-4366-a5e5-c209d4352b2d",
"title": "The Phoenix Project",
"title": "{J} The Phoenix Project",
"isbnNumber": "978-1942788294",
"edition": {
"number": 5,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"something_to_be_ignored": "some_value",
"title": "The Phoenix Project",
"title": "{J} The Phoenix Project",
"isbnNumber": "978-1942788294",
"edition": {
"number": 5,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<book isbnNumber="978-1942788294" ignoreMe="something">
<ignored>thing</ignored>
<title>The Phoenix Project</title>
<title>{X} The Phoenix Project</title>
<edition number="5">5th Anniversary Limited Edition</edition>
<publishDate>2018-04-16</publishDate>
<author>Kevin Behr</author>
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-camel-case.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "f3758bf0-0ff7-4366-a5e5-c209d4352b2d",
"title": "The Phoenix Project",
"title": "{J} The Phoenix Project",
"isbnNumber": "978-1942788294",
"edition": {
"number": 5,
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-kebab-case-1-v2.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<book isbn-number="978-1942788294">
<id>f3758bf0-0ff7-4366-a5e5-c209d4352b2d</id>
<title>The Phoenix Project</title>
<title>{X} The Phoenix Project</title>
<edition number="5">5th Anniversary Limited Edition</edition>
<publish-date>2018-04-16</publish-date>
<author>Kevin Behr</author>
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-kebab-case-1.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<book isbn-number="978-1942788294">
<id>f3758bf0-0ff7-4366-a5e5-c209d4352b2d</id>
<title>The Phoenix Project</title>
<title>{X} The Phoenix Project</title>
<edition number="5">5th Anniversary Limited Edition</edition>
<publish-date>2018-04-16</publish-date>
<author>Kevin Behr</author>
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-kebab-case.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "f3758bf0-0ff7-4366-a5e5-c209d4352b2d",
"title": "The Phoenix Project",
"title": "{J} The Phoenix Project",
"isbn-number": "978-1942788294",
"edition": {
"number": 5,
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-snake-case-1.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<book isbn_number="978-1942788294">
<id>f3758bf0-0ff7-4366-a5e5-c209d4352b2d</id>
<title>The Phoenix Project</title>
<title>{X} The Phoenix Project</title>
<edition number="5">5th Anniversary Limited Edition</edition>
<publish_date>2018-04-16</publish_date>
<author>Kevin Behr</author>
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/the-phoenix-project-snake-case.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "f3758bf0-0ff7-4366-a5e5-c209d4352b2d",
"title": "The Phoenix Project",
"title": "{J} The Phoenix Project",
"isbn_number": "978-1942788294",
"edition": {
"number": 5,
Expand Down
21 changes: 21 additions & 0 deletions tests/model.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# encoding: utf-8
import re

# This file is part of py-serializable
#
Expand Down Expand Up @@ -68,6 +69,25 @@ def deserialize(cls, o: object) -> Set["BookReference"]:
raise ValueError(f'Attempt to deserialize a non-set: {o.__class__}')


class TitleMapper(BaseHelper):

@classmethod
def json_serialize(cls, o: str) -> str:
return f'{{J}} {o}'

@classmethod
def json_deserialize(cls, o: str) -> str:
return re.sub(r'^\{J} ', '', o)

@classmethod
def xml_serialize(cls, o: str) -> str:
return f'{{X}} {o}'

@classmethod
def xml_deserialize(cls, o: str) -> str:
return re.sub(r'^\{X} ', '', o)


@serializable.serializable_class
class Chapter:

Expand Down Expand Up @@ -223,6 +243,7 @@ def id(self) -> UUID:

@property # type: ignore[misc]
@serializable.xml_sequence(2)
@serializable.type_mapping(TitleMapper)
def title(self) -> str:
return self._title

Expand Down

0 comments on commit dc998df

Please sign in to comment.