Skip to content

Add Default Serializer for Extended Types #46

@yaythomas

Description

@yaythomas

Context

Current default serializer is JSON, but it doesn't handle common Python types like datetime, decimal, bytes, uuid, tuple.

The idea is to keep the "plain" JSON serializer in there as a fallback for smaller payloads customers can elect to use, and then using this extended serializer that uses an envelope for more comprehensive serialization at the cost of higher storage.

Technical Requirements

  • Extend default JSON serializer to handle additional types
  • Maintain backward compatibility
  • Ensure round-trip serialization works
  • Provide clear error messages for unsupported types

Implementation Details

  • File: src/aws_durable_execution_sdk_python/serdes.py
  • Extended Types: datetime, Decimal, bytes, UUID, tuple
import json
from datetime import datetime, date
from decimal import Decimal
import uuid
# --- Minimal wrapper keys ---
WRAPPER_KEY = "_"
TYPE_KEY = "t"
VALUE_KEY = "v"
# --- Iterative serializer ---
def serialize(obj):
    root = {WRAPPER_KEY: None}
    stack = [(root, WRAPPER_KEY, obj)]  # (parent, key, obj_to_process)
    while stack:
        parent, key, current = stack.pop()
        # Special types
        if isinstance(current, tuple):
            val = [None] * len(current)
            parent[key] = {WRAPPER_KEY: {TYPE_KEY: "tuple", VALUE_KEY: val}}
            for i, item in reversed(list(enumerate(current))):
                stack.append((val, i, item))
        elif isinstance(current, datetime):
            parent[key] = {WRAPPER_KEY: {TYPE_KEY: "datetime", VALUE_KEY: current.isoformat()}}
        elif isinstance(current, date):
            parent[key] = {WRAPPER_KEY: {TYPE_KEY: "date", VALUE_KEY: current.isoformat()}}
        elif isinstance(current, Decimal):
            parent[key] = {WRAPPER_KEY: {TYPE_KEY: "decimal", VALUE_KEY: str(current)}}
        elif isinstance(current, uuid.UUID):
            parent[key] = {WRAPPER_KEY: {TYPE_KEY: "uuid", VALUE_KEY: str(current)}}
        # Containers
        elif isinstance(current, dict):
            val = {}
            parent[key] = {WRAPPER_KEY: {TYPE_KEY: "dict", VALUE_KEY: val}}
            for k, v in reversed(list(current.items())):
                stack.append((val, k, v))
        elif isinstance(current, list):
            val = [None] * len(current)
            parent[key] = {WRAPPER_KEY: {TYPE_KEY: "list", VALUE_KEY: val}}
            for i, item in reversed(list(enumerate(current))):
                stack.append((val, i, item))
        # Primitives
        elif current is None:
            parent[key] = {WRAPPER_KEY: {TYPE_KEY: "none", VALUE_KEY: None}}
        elif isinstance(current, (str, int, float, bool)):
            parent[key] = {WRAPPER_KEY: {TYPE_KEY: type(current).__name__, VALUE_KEY: current}}
        else:
            raise TypeError(f"Unsupported type: {type(current)}")
    return json.dumps(root, separators=(",", ":"))

Acceptance Criteria

  • Default serializer handles extended types
  • Round-trip serialization works correctly
  • Backward compatibility maintained
  • Clear error messages for unsupported types
  • Performance impact is minimal

Priority: Medium
Estimated Effort: 2 days

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions