Skip to content
This repository has been archived by the owner on Oct 14, 2024. It is now read-only.

fix serialization of pendulum.DateTime objects in JsonSerializationWriter #369

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.3.2] - 2024-09-10
## [1.3.3] - 2024-10-03

### Added
### Fixed

- Fixed numeric strings from being parsed as Datetime objects to being parsed as strings.
-Only parse to Datetime objects that conform to ISO 8601 format.
- Fixed an issue where `pendulum.DateTime` objects were not properly serialized by `JsonSerializationWriter`. Now, `pendulum.DateTime` objects are correctly recognized as subclasses of `datetime.datetime` and serialized accordingly.

## [1.3.2] - 2024-09-10

### Added

- Fixed numeric strings from being parsed as Datetime objects to being parsed as strings.
- Only parse to Datetime objects that conform to ISO 8601 format.

## [1.3.1] - 2024-08-23

### Added

- Fixed 4-digit numeric strings from being parsed as Datetime objects to being parsed as strings.


## [1.3.0] - 2024-07-26

### Added
Expand All @@ -37,62 +40,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

### Changed

- Enhanced error handling: Enabled silent failure when an enum key is not available

## [1.1.0] - 2024-02-29

### Added

### Changed

- Support objects and collections when writing additional data.

## [1.0.1] - 2023-12-16

### Added

### Changed

- Bump pendulum to v3.0.0b1 for python 3.12 support.

## [1.0.0] - 2023-10-31

### Added

### Changed

- GA release

## [0.4.2] - 2023-10-11

### Added

### Changed

- Switched from python-dateutil to pendulum for parsing datetime types.

## [0.4.1] - 2023-09-21

### Added

### Changed

- Allow passing of valid strings as values for datetime and UUID fields.

## [0.4.0] - 2023-07-27

### Added

### Changed

- Enabled backing store support

## [0.3.7] - 2023-07-04

### Added

### Changed

- Fixes the key assignment to the writer in write_bytes_value.

## [0.3.6] - 2023-06-27

### Added

### Changed

- Fixed a bug with loading json response in method to get root parse node.

## [0.3.5] - 2023-06-14
Expand Down Expand Up @@ -153,4 +165,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Fixed a bug with deserializing 'None' string values in enums.

2 changes: 1 addition & 1 deletion kiota_serialization_json/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION: str = '1.3.2'
VERSION: str = '1.3.3'
46 changes: 23 additions & 23 deletions kiota_serialization_json/json_serialization_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@


class JsonSerializationWriter(SerializationWriter):

PROPERTY_SEPARATOR: str = ','

def __init__(self) -> None:
self.writer: Dict = {}
self.value: Any = None

self._on_start_object_serialization: Optional[Callable[[Parsable, SerializationWriter],
None]] = None
None]] = None
self._on_before_object_serialization: Optional[Callable[[Parsable], None]] = None
self._on_after_object_serialization: Optional[Callable[[Parsable], None]] = None

Expand Down Expand Up @@ -192,7 +191,7 @@ def write_time_value(self, key: Optional[str], value: Optional[time]) -> None:
raise ValueError("Invalid time string value found")

def write_collection_of_primitive_values(
self, key: Optional[str], values: Optional[List[T]]
self, key: Optional[str], values: Optional[List[T]]
) -> None:
"""Writes the specified collection of primitive values to the stream with an optional
given key.
Expand All @@ -213,7 +212,7 @@ def write_collection_of_primitive_values(
self.value = result

def write_collection_of_object_values(
self, key: Optional[str], values: Optional[List[U]]
self, key: Optional[str], values: Optional[List[U]]
) -> None:
"""Writes the specified collection of model objects to the stream with an optional
given key.
Expand All @@ -234,7 +233,7 @@ def write_collection_of_object_values(
self.value = obj_list

def write_collection_of_enum_values(
self, key: Optional[str], values: Optional[List[Enum]]
self, key: Optional[str], values: Optional[List[Enum]]
) -> None:
"""Writes the specified collection of enum values to the stream with an optional given key.
Args:
Expand All @@ -254,7 +253,7 @@ def write_collection_of_enum_values(
self.value = result

def __write_collection_of_dict_values(
self, key: Optional[str], values: Optional[List[Dict[str, Any]]]
self, key: Optional[str], values: Optional[List[Dict[str, Any]]]
) -> None:
"""Writes the specified collection of dictionary values to the stream with an optional
given key.
Expand Down Expand Up @@ -291,7 +290,7 @@ def write_bytes_value(self, key: Optional[str], value: bytes) -> None:
self.value = base64_string

def write_object_value(
self, key: Optional[str], value: Optional[U], *additional_values_to_merge: U
self, key: Optional[str], value: Optional[U], *additional_values_to_merge: U
) -> None:
"""Writes the specified model object to the stream with an optional given key.
Args:
Expand Down Expand Up @@ -424,7 +423,7 @@ def on_after_object_serialization(self, value: Optional[Callable[[Parsable], Non

@property
def on_start_object_serialization(
self
self
) -> Optional[Callable[[Parsable, SerializationWriter], None]]:
"""Gets the callback called right after the serialization process starts.
Returns:
Expand All @@ -435,7 +434,7 @@ def on_start_object_serialization(

@on_start_object_serialization.setter
def on_start_object_serialization(
self, value: Optional[Callable[[Parsable, SerializationWriter], None]]
self, value: Optional[Callable[[Parsable, SerializationWriter], None]]
) -> None:
"""Sets the callback called right after the serialization process starts.
Args:
Expand All @@ -458,41 +457,42 @@ def write_non_parsable_object_value(self, key: Optional[str], value: T) -> None:

def write_any_value(self, key: Optional[str], value: Any) -> Any:
"""Writes the specified value to the stream with an optional given key.

Args:
key (Optional[str]): The key to be used for the written value. May be null.
value Any): The value to be written.
value (Any): The value to be written.
"""
value_type = type(value)
if value is None:
self.write_null_value(key)
elif value_type in PRIMITIVE_TYPES:
method = getattr(self, f'write_{value_type.__name__.lower()}_value')
method(key, value)
elif isinstance(value, Parsable):
self.write_object_value(key, value)
elif isinstance(value, list):
if all(isinstance(x, Parsable) for x in value):
self.write_collection_of_object_values(key, value)
elif all(isinstance(x, Enum) for x in value):
self.write_collection_of_enum_values(key, value)
elif all((type(x) in PRIMITIVE_TYPES) for x in value):
elif all(any(isinstance(x, primitive_type) for primitive_type in PRIMITIVE_TYPES) for x in value):
self.write_collection_of_primitive_values(key, value)
elif all(isinstance(x, dict) for x in value):
self.__write_collection_of_dict_values(key, value)
else:
raise TypeError(
f"Encountered an unknown collection type during serialization \
{value_type} with key {key}"
f"Encountered an unknown collection type during serialization {type(value)} with key {key}"
)
elif isinstance(value, dict):
self.__write_dict_value(key, value)
elif hasattr(value, '__dict__'):
self.write_non_parsable_object_value(key, value)
else:
raise TypeError(
f"Encountered an unknown type during serialization {value_type} \
with key {key}"
)
for primitive_type in PRIMITIVE_TYPES:
if isinstance(value, primitive_type):
method = getattr(self, f"write_{primitive_type.__name__.lower()}_value")
method(key, value)
return
if hasattr(value, "__dict__"):
self.write_non_parsable_object_value(key, value)
else:
raise TypeError(
f"Encountered an unknown type during serialization {type(value)} with key {key}"
)

def _serialize_value(self, temp_writer: JsonSerializationWriter, value: U):
if on_before := self.on_before_object_serialization:
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/test_json_serialization_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ def test_write_additional_data_value(user_1, user_2):
"manager": user_1,
"approvers": [user_1, user_2],
"created_at": date(2022, 1, 27),
"updated_at": pendulum.DateTime(2024, 9, 24),
"data": {
"groups": [{
"friends": [user_2]
Expand All @@ -271,5 +272,7 @@ def test_write_additional_data_value(user_1, user_2):
'"updated_at": "2022-01-27T12:59:45.596117+00:00", "is_active": true}, '\
'"approvers": [{"id": "8f841f30-e6e3-439a-a812-ebd369559c36", '\
'"updated_at": "2022-01-27T12:59:45.596117+00:00", "is_active": true}, '\
'{"display_name": "John Doe", "age": 32}], "created_at": "2022-01-27", '\
'"data": {"groups": [{"friends": [{"display_name": "John Doe", "age": 32}]}]}}'
'{"display_name": "John Doe", "age": 32}], '\
'"created_at": "2022-01-27", '\
'"updated_at": "2024-09-24T00:00:00", '\
'"data": {"groups": [{"friends": [{"display_name": "John Doe", "age": 32}]}]}}'
Loading