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

Commit

Permalink
Merge pull request #369 from eran-av/bugfix/pendulum-datetime-seriali…
Browse files Browse the repository at this point in the history
…zation

fix serialization of pendulum.DateTime objects in JsonSerializationWriter
  • Loading branch information
baywet authored Oct 3, 2024
2 parents d9bd14c + 61c3e39 commit 0fff987
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 90 deletions.
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'
64 changes: 34 additions & 30 deletions kiota_serialization_json/json_serialization_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@


class JsonSerializationWriter(SerializationWriter):

PROPERTY_SEPARATOR: str = ','

def __init__(self) -> None:
Expand Down Expand Up @@ -80,7 +79,7 @@ def write_uuid_value(self, key: Optional[str], value: Optional[UUID]) -> None:
"""Writes the specified uuid 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 (Optional[UUId]): The uuid value to be written.
value (Optional[UUID]): The uuid value to be written.
"""
if isinstance(value, UUID):
if key:
Expand All @@ -107,9 +106,9 @@ def write_datetime_value(self, key: Optional[str], value: Optional[datetime]) ->
"""
if isinstance(value, datetime):
if key:
self.writer[key] = str(value.isoformat())
self.writer[key] = value.isoformat()
else:
self.value = str(value.isoformat())
self.value = value.isoformat()
elif isinstance(value, str):
try:
pendulum.parse(value)
Expand Down Expand Up @@ -239,7 +238,7 @@ def write_collection_of_enum_values(
"""Writes the specified collection of enum values to the stream with an optional given key.
Args:
key (Optional[str]): The key to be used for the written value. May be null.
values Optional[List[Enum]): The enum values to be written.
values (Optional[List[Enum]]): The enum values to be written.
"""
if isinstance(values, list):
result = []
Expand Down Expand Up @@ -360,7 +359,7 @@ def __write_dict_value(self, key: Optional[str], value: Dict[str, Any]) -> None:
def write_additional_data_value(self, value: Dict[str, Any]) -> None:
"""Writes the specified additional data to the stream.
Args:
value (Dict[str, Any]): he additional data to be written.
value (Dict[str, Any]): The additional data to be written.
"""
if isinstance(value, dict):
for key, val in value.items():
Expand Down Expand Up @@ -390,35 +389,35 @@ def get_serialized_content(self) -> bytes:
def on_before_object_serialization(self) -> Optional[Callable[[Parsable], None]]:
"""Gets the callback called before the object gets serialized.
Returns:
Optional[Callable[[Parsable], None]]:the callback called before the object
Optional[Callable[[Parsable], None]]: The callback called before the object
gets serialized.
"""
return self._on_before_object_serialization

@on_before_object_serialization.setter
def on_before_object_serialization(self, value: Optional[Callable[[Parsable], None]]) -> None:
"""Sets the callback called before the objects gets serialized.
"""Sets the callback called before the objects get serialized.
Args:
value (Optional[Callable[[Parsable], None]]): the callback called before the objects
gets serialized.
value (Optional[Callable[[Parsable], None]]): The callback called before the objects
get serialized.
"""
self._on_before_object_serialization = value

@property
def on_after_object_serialization(self) -> Optional[Callable[[Parsable], None]]:
"""Gets the callback called after the object gets serialized.
Returns:
Optional[Optional[Callable[[Parsable], None]]]: the callback called after the object
Optional[Callable[[Parsable], None]]: The callback called after the object
gets serialized.
"""
return self._on_after_object_serialization

@on_after_object_serialization.setter
def on_after_object_serialization(self, value: Optional[Callable[[Parsable], None]]) -> None:
"""Sets the callback called after the objects gets serialized.
"""Sets the callback called after the objects get serialized.
Args:
value (Optional[Callable[[Parsable], None]]): the callback called after the objects
gets serialized.
value (Optional[Callable[[Parsable], None]]): The callback called after the objects
get serialized.
"""
self._on_after_object_serialization = value

Expand All @@ -428,7 +427,7 @@ def on_start_object_serialization(
) -> Optional[Callable[[Parsable, SerializationWriter], None]]:
"""Gets the callback called right after the serialization process starts.
Returns:
Optional[Callable[[Parsable, SerializationWriter], None]]: the callback called
Optional[Callable[[Parsable, SerializationWriter], None]]: The callback called
right after the serialization process starts.
"""
return self._on_start_object_serialization
Expand All @@ -439,7 +438,7 @@ def on_start_object_serialization(
) -> None:
"""Sets the callback called right after the serialization process starts.
Args:
value (Optional[Callable[[Parsable, SerializationWriter], None]]): the callback
value (Optional[Callable[[Parsable, SerializationWriter], None]]): The callback
called right after the serialization process starts.
"""
self._on_start_object_serialization = value
Expand All @@ -458,41 +457,46 @@ 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
2 changes: 1 addition & 1 deletion tests/unit/test_json_parse_node_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def test_get_root_parse_node(sample_json_string):
assert isinstance(root, JsonParseNode)
assert root._json_node == json.loads(sample_json_string)


def test_get_root_parse_node_no_content_type(sample_json_string):
with pytest.raises(Exception) as e_info:
factory = JsonParseNodeFactory()
Expand Down
Loading

0 comments on commit 0fff987

Please sign in to comment.