Claude Code Plugins

Community-maintained marketplace

Feedback

patterns-serialization-dev

@aRustyDev/ai
0
0

Cross-cutting patterns for serialization and validation across languages. Use when translating JSON handling between languages, converting struct tags to derive macros, mapping validation libraries, or designing schema-based serialization for language conversions.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name patterns-serialization-dev
description Cross-cutting patterns for serialization and validation across languages. Use when translating JSON handling between languages, converting struct tags to derive macros, mapping validation libraries, or designing schema-based serialization for language conversions.

Serialization Patterns

Cross-language reference for serialization, deserialization, and validation patterns. This skill helps translate data handling patterns between languages during code conversion.

Overview

This skill covers:

  • JSON/YAML/TOML serialization comparison
  • Struct tags, derive macros, dataclasses
  • Validation library mappings
  • Schema generation and enforcement
  • Custom serializer/deserializer patterns

This skill does NOT cover:

  • Building applications with serialization (see lang-*-dev skills)
  • Protocol buffers/gRPC (see dedicated skills)
  • Database ORM mapping (see database-specific skills)

Serialization Mechanism Comparison

Language Primary Approach Configuration Validation
TypeScript Class decorators class-transformer class-validator, zod
Python Dataclasses/Pydantic Type hints + decorators Built into Pydantic
Rust Derive macros #[serde(...)] attributes validator crate
Go Struct tags `json:"name"` validator package
Java/Kotlin Annotations @JsonProperty Bean Validation
C# Attributes [JsonPropertyName] Data Annotations

JSON Serialization by Language

TypeScript

// class-transformer + class-validator
import { Type, Expose, Transform } from 'class-transformer';
import { IsEmail, Length, IsOptional } from 'class-validator';

class User {
  @Expose({ name: 'user_id' })
  id: string;

  @Length(1, 100)
  name: string;

  @IsEmail()
  @IsOptional()
  email?: string;

  @Type(() => Date)
  @Transform(({ value }) => new Date(value), { toClassOnly: true })
  createdAt: Date;
}

// Usage
const user = plainToInstance(User, jsonData);
const errors = await validate(user);

Python (Pydantic)

from pydantic import BaseModel, Field, EmailStr, field_validator
from datetime import datetime
from typing import Optional

class User(BaseModel):
    id: str = Field(alias='user_id')
    name: str = Field(min_length=1, max_length=100)
    email: Optional[EmailStr] = None
    created_at: datetime

    @field_validator('name')
    @classmethod
    def name_must_not_be_empty(cls, v: str) -> str:
        if not v.strip():
            raise ValueError('name cannot be empty')
        return v

    class Config:
        populate_by_name = True  # Allow both 'id' and 'user_id'

# Usage
user = User.model_validate(json_data)
json_str = user.model_dump_json()

Python (dataclasses + dacite)

from dataclasses import dataclass, field
from typing import Optional
from datetime import datetime
from dacite import from_dict, Config

@dataclass
class User:
    id: str
    name: str
    email: Optional[str] = None
    created_at: datetime = field(default_factory=datetime.now)

# Usage
user = from_dict(User, json_data, config=Config(
    cast=[datetime],
    strict=True
))

Rust (Serde)

use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use validator::Validate;

#[derive(Debug, Serialize, Deserialize, Validate)]
#[serde(rename_all = "snake_case")]
struct User {
    #[serde(rename = "user_id")]
    id: String,

    #[validate(length(min = 1, max = 100))]
    name: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[validate(email)]
    email: Option<String>,

    #[serde(with = "chrono::serde::ts_seconds")]
    created_at: DateTime<Utc>,
}

// Usage
let user: User = serde_json::from_str(&json_str)?;
user.validate()?;
let json = serde_json::to_string(&user)?;

Go

import (
    "encoding/json"
    "time"

    "github.com/go-playground/validator/v10"
)

type User struct {
    ID        string    `json:"user_id"`
    Name      string    `json:"name" validate:"required,min=1,max=100"`
    Email     *string   `json:"email,omitempty" validate:"omitempty,email"`
    CreatedAt time.Time `json:"created_at"`
}

// Usage
var user User
err := json.Unmarshal(jsonData, &user)

validate := validator.New()
err = validate.Struct(user)

Java (Jackson)

import com.fasterxml.jackson.annotation.*;
import jakarta.validation.constraints.*;
import java.time.Instant;

public class User {
    @JsonProperty("user_id")
    private String id;

    @NotBlank
    @Size(min = 1, max = 100)
    private String name;

    @Email
    private String email;

    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private Instant createdAt;

    // getters, setters...
}

// Usage
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(jsonStr, User.class);

Field Mapping Translation

Rename Fields

Language Syntax Example
TypeScript @Expose({ name: 'x' }) @Expose({ name: 'user_id' })
Python Field(alias='x') id: str = Field(alias='user_id')
Rust #[serde(rename = "x")] #[serde(rename = "user_id")]
Go `json:"x"` `json:"user_id"`
Java @JsonProperty("x") @JsonProperty("user_id")

Case Conversion

Language Syntax Result
TypeScript Manual or transformer N/A
Python model_config = ConfigDict(alias_generator=to_camel) userId
Rust #[serde(rename_all = "camelCase")] userId
Go `json:"userId"` (manual) userId
Java @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) user_id

Skip Null/Empty

Language Syntax Effect
TypeScript @Exclude() with condition Excludes field
Python exclude_none=True in model_dump() Skips None
Rust #[serde(skip_serializing_if = "Option::is_none")] Skips None
Go json:",omitempty" Skips zero values
Java @JsonInclude(Include.NON_NULL) Skips null

Validation Pattern Translation

String Validation

Validation TypeScript Python Rust Go
Required @IsNotEmpty() str (not Optional) Not Option validate:"required"
Min length @Length(min, max) Field(min_length=n) #[validate(length(min = n))] validate:"min=n"
Max length @Length(min, max) Field(max_length=n) #[validate(length(max = n))] validate:"max=n"
Regex @Matches(regex) Field(pattern=r'...') #[validate(regex = "...")] validate:"regexp=..."
Email @IsEmail() EmailStr #[validate(email)] validate:"email"
URL @IsUrl() HttpUrl #[validate(url)] validate:"url"

Numeric Validation

Validation TypeScript Python Rust Go
Min value @Min(n) Field(ge=n) #[validate(range(min = n))] validate:"gte=n"
Max value @Max(n) Field(le=n) #[validate(range(max = n))] validate:"lte=n"
Positive @IsPositive() Field(gt=0) #[validate(range(min = 1))] validate:"gt=0"
Negative @IsNegative() Field(lt=0) #[validate(range(max = -1))] validate:"lt=0"

Custom Validation

TypeScript:

@ValidatorConstraint()
class IsAdultConstraint implements ValidatorConstraintInterface {
  validate(age: number) {
    return age >= 18;
  }
  defaultMessage() {
    return 'Must be 18 or older';
  }
}

@Validate(IsAdultConstraint)
age: number;

Python (Pydantic):

@field_validator('age')
@classmethod
def must_be_adult(cls, v: int) -> int:
    if v < 18:
        raise ValueError('Must be 18 or older')
    return v

Rust:

fn validate_adult(age: &i32) -> Result<(), validator::ValidationError> {
    if *age < 18 {
        return Err(validator::ValidationError::new("must_be_adult"));
    }
    Ok(())
}

#[validate(custom(function = "validate_adult"))]
age: i32,

Go:

func isAdult(fl validator.FieldLevel) bool {
    return fl.Field().Int() >= 18
}

validate.RegisterValidation("adult", isAdult)

type Person struct {
    Age int `validate:"adult"`
}

Nested Object Handling

TypeScript

class Address {
  @IsString()
  street: string;
}

class User {
  @ValidateNested()
  @Type(() => Address)
  address: Address;

  @ValidateNested({ each: true })
  @Type(() => Address)
  addresses: Address[];
}

Python

class Address(BaseModel):
    street: str

class User(BaseModel):
    address: Address
    addresses: list[Address]

Rust

#[derive(Serialize, Deserialize, Validate)]
struct Address {
    street: String,
}

#[derive(Serialize, Deserialize, Validate)]
struct User {
    #[validate(nested)]
    address: Address,

    #[validate(nested)]
    addresses: Vec<Address>,
}

Go

type Address struct {
    Street string `json:"street" validate:"required"`
}

type User struct {
    Address   Address   `json:"address" validate:"required"`
    Addresses []Address `json:"addresses" validate:"dive"`
}

Default Values

Language Syntax Example
TypeScript Property initializer status: string = 'pending'
Python Field(default=...) status: str = Field(default='pending')
Rust #[serde(default)] #[serde(default = "default_status")]
Go Not in struct tags Manual in constructor
Java Field initializer private String status = "pending";

Rust default function:

fn default_status() -> String {
    "pending".to_string()
}

#[derive(Deserialize)]
struct Order {
    #[serde(default = "default_status")]
    status: String,
}

Library Mapping

Serialization Libraries

Category TypeScript Python Rust Go
JSON Built-in json serde_json encoding/json
YAML js-yaml pyyaml serde_yaml gopkg.in/yaml.v3
TOML @iarna/toml tomli/tomllib toml github.com/BurntSushi/toml
MessagePack @msgpack/msgpack msgpack rmp-serde github.com/vmihailenco/msgpack

Validation Libraries

TypeScript Python Rust Go Java
class-validator Pydantic (built-in) validator go-playground/validator Bean Validation
zod pydantic garde ozzo-validation Hibernate Validator
yup cerberus - - -
joi marshmallow - - -

Schema Generation

TypeScript Python Rust Go
typescript-json-schema pydantic (built-in) schemars github.com/invopop/jsonschema
zod-to-json-schema datamodel-code-generator - -

Common Patterns

Optional vs Required

TypeScript:  name?: string          → Optional
Python:      name: Optional[str]    → Optional
Rust:        name: Option<String>   → Optional
Go:          Name *string           → Optional (pointer)

Date/Time Handling

Language Type JSON Format Custom Format
TypeScript Date ISO string @Transform()
Python datetime ISO string @field_serializer()
Rust DateTime<Utc> ISO string #[serde(with = "...")]
Go time.Time RFC3339 Custom MarshalJSON

Enums

TypeScript:

enum Status { Pending = 'pending', Active = 'active' }

class Order {
  @IsEnum(Status)
  status: Status;
}

Python:

from enum import Enum

class Status(str, Enum):
    pending = 'pending'
    active = 'active'

class Order(BaseModel):
    status: Status

Rust:

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum Status {
    Pending,
    Active,
}

Go:

type Status string

const (
    StatusPending Status = "pending"
    StatusActive  Status = "active"
)

Anti-Patterns

1. Mixing Validation and Business Logic

# ❌ Business logic in validator
@field_validator('age')
def check_age(cls, v):
    if v < 18:
        send_notification("underage user attempted")  # Side effect!
        raise ValueError('underage')
    return v

# ✓ Separate concerns
@field_validator('age')
def check_age(cls, v):
    if v < 18:
        raise ValueError('underage')
    return v

# Business logic elsewhere
if not user.is_adult():
    send_notification(...)

2. Over-validation

# ❌ Validating internal types
class InternalData(BaseModel):
    # This is only used internally, no need for extensive validation
    temp_id: str = Field(regex=r'^[a-f0-9]{32}$')

# ✓ Validate at boundaries only
class UserInput(BaseModel):  # External input
    email: EmailStr  # Validate here

class InternalData:  # Internal use
    temp_id: str  # Trust internal code

3. Ignoring Serialization Errors

// ❌ Ignoring errors
json.Unmarshal(data, &user)  // Error ignored!

// ✓ Handle errors
if err := json.Unmarshal(data, &user); err != nil {
    return fmt.Errorf("invalid user data: %w", err)
}

Best Practices

  1. Validate at boundaries - Only validate external input, trust internal types
  2. Use schema-first when possible for API contracts
  3. Prefer declarative over imperative validation
  4. Keep serialization pure - No side effects in serializers
  5. Document format expectations in struct/class comments
  6. Test edge cases - Empty strings, nulls, malformed dates
  7. Version your schemas for backward compatibility

Related Skills

  • meta-convert-dev - Code conversion patterns
  • patterns-metaprogramming-dev - Decorators/macros used for serialization
  • lang-*-dev skills - Language-specific serialization details