Skip to content

mcncl/gotyper

Repository files navigation

GoTyper

CI codecov Go Version License: MIT GitHub release (latest by date)

GoTyper

A powerful command-line tool that generates Go structs from JSON with comprehensive multi-format tag support (JSON, YAML, XML) and advanced customization options.

Features

Core Functionality

  • Converts JSON from files or stdin to Go structs
  • Interactive mode for quick, ad-hoc conversions
  • Intelligent type detection including UUIDs and timestamps
  • Handles complex nested objects and arrays
  • Formats output according to Go standards

Multi-Format Tag Support

  • JSON tags: Standard Go JSON serialization tags
  • YAML tags: Generate YAML-compatible struct tags
  • XML tags: Generate XML serialization tags
  • Simultaneous generation: Create multiple tag formats at once

Advanced Customization

  • Configuration files: Use .gotyper.yml for project-specific settings
  • Pattern-based field customization: Apply custom tag options using regex patterns
  • Field skipping: Exclude sensitive or internal fields from struct generation
  • Type mappings: Map specific field patterns to custom Go types
  • Naming conventions: Customize field and struct naming rules
  • Tag customization: Control omitempty, field exclusion, and custom serialization options

Installation

go install github.com/mcncl/gotyper@latest

Usage

Basic Usage

# From a file
gotyper -i input.json -o output.go

# From stdin
cat input.json | gotyper > output.go

# Interactive mode (just run gotyper with no arguments)
gotyper
# Then paste your JSON and press Ctrl+D (or Ctrl+Z on Windows) when done

Command Line Options

  -i, --input=STRING     Path to input JSON file. If not specified, reads from stdin.
  -o, --output=STRING    Path to output Go file. If not specified, writes to stdout.
  -p, --package=STRING   Package name for generated code. (default: main)
  -r, --root-name=STRING Name for the root struct. (default: RootType)
  -c, --config=STRING    Path to configuration file. If not specified, searches for .gotyper.yml
  -f, --format           Format the output code according to Go standards. (default: true)
  -d, --debug            Enable debug logging.
  -v, --version          Show version information.
  -I, --interactive      Run in interactive mode, allowing direct JSON input with Ctrl+D to process.

Configuration Files

GoTyper supports YAML configuration files for advanced customization. The tool automatically searches for .gotyper.yml, .gotyper.yaml, gotyper.yml, or gotyper.yaml in the current directory and parent directories.

Example Configuration: See .gotyper.example.yml for a comprehensive configuration example with all available options.

Basic Configuration

Create a .gotyper.yml file in your project root:

package: "models"
root_name: "APIResponse"

# Generate multiple tag formats
json_tags:
  omitempty_for_pointers: true
  omitempty_for_slices: true
  additional_tags:
    - "yaml"
    - "xml"

Advanced Configuration

package: "models"
root_name: "APIResponse"

# Type mappings for consistent field types
types:
  mappings:
    - pattern: ".*_id$|^id$"
      type: "int64"
      comment: "Database ID"
    - pattern: "created_at|updated_at|.*_time$"
      type: "time.Time"
      import: "time"
      comment: "Timestamp"

# Custom field naming
naming:
  pascal_case_fields: true
  field_mappings:
    "user_id": "UserID"
    "api_key": "APIKey"
    "url": "URL"

# Enhanced tag generation
json_tags:
  omitempty_for_pointers: true
  omitempty_for_slices: true
  additional_tags:
    - "yaml"
    - "xml"
  
  # Pattern-based tag customization
  custom_options:
    - pattern: "password.*|.*secret.*"
      options: "-"
      comment: "Sensitive field - excluded from JSON"
    - pattern: ".*_count$|.*_total$"
      options: "omitempty,string"
      comment: "Numeric field serialized as string"
  
  # Fields to skip entirely
  skip_fields:
    - "internal_use_only"
    - "debug_info"

Key Features Explained

Working with Root Structs

The --root-name flag allows you to specify a custom name for the root struct:

# Generate a struct named "User" instead of "RootType"
gotyper -i user.json -r User

When processing JSON objects, this name is used directly:

{
  "name": "John",
  "email": "[email protected]"
}

With --root-name=User generates:

type User struct {
  Email string `json:"email"`
  Name  string `json:"name"`
}

When processing arrays at the root level, the root name is automatically singularized to name the struct for array elements:

[
  { "id": 1, "name": "Item 1" },
  { "id": 2, "name": "Item 2" }
]

With --root-name=Items generates:

type Item struct { // Note: 'Items' is singularized to 'Item'
  ID   int64  `json:"id"`
  Name string `json:"name"`
}

Package Name

The --package flag sets the package declaration in the generated Go file:

# Generate code for a specific package
gotyper -i data.json -p models

Code Formatting

By default, GoTyper formats the output code according to Go standards. You can disable this with --format=false:

# Disable automatic formatting
gotyper -i data.json --format=false

Interactive Mode

For quick, ad-hoc conversions without creating temporary files:

  1. Run gotyper with no arguments (or with the -I flag)
  2. Paste your JSON data at the prompt
  3. Press Ctrl+D (Unix/Mac) or Ctrl+Z followed by Enter (Windows) to signal the end of input
  4. The generated Go structs will be displayed immediately

Examples

Basic JSON-to-Go Conversion

Input JSON:

{
  "name": "John Doe",
  "age": 30,
  "email": "[email protected]",
  "is_active": true,
  "created_at": "2023-01-01T12:00:00Z",
  "address": {
    "street": "123 Main St",
    "city": "Anytown",
    "zip": "12345"
  },
  "tags": ["developer", "golang"],
  "scores": [98, 87, 95]
}

Output Go Code (default):

package main

import (
	"time"
)

type Address struct {
	City   string `json:"city"`
	Street string `json:"street"`
	Zip    string `json:"zip"`
}

type RootType struct {
	Address   *Address  `json:"address,omitempty"`
	Age       int64     `json:"age"`
	CreatedAt time.Time `json:"created_at"`
	Email     string    `json:"email"`
	IsActive  bool      `json:"is_active"`
	Name      string    `json:"name"`
	Scores    []int64   `json:"scores,omitempty"`
	Tags      []string  `json:"tags,omitempty"`
}

Multi-Format Tags Example

Configuration (.gotyper.yml):

package: "models"
root_name: "User"

json_tags:
  additional_tags:
    - "yaml"
    - "xml"
  custom_options:
    - pattern: ".*email.*"
      options: "omitempty"
      comment: "Email address"

Enhanced Output:

package models

import (
	"time"
)

type Address struct {
	City   string `json:"city" yaml:"city" xml:"city"`
	Street string `json:"street" yaml:"street" xml:"street"`
	Zip    string `json:"zip" yaml:"zip" xml:"zip"`
}

type User struct {
	Address   *Address  `json:"address,omitempty" yaml:"address" xml:"address"`
	Age       int64     `json:"age" yaml:"age" xml:"age"`
	CreatedAt time.Time `json:"created_at" yaml:"created_at" xml:"created_at"`
	Email     string    `json:"email,omitempty" yaml:"email" xml:"email"` // Email address
	IsActive  bool      `json:"is_active" yaml:"is_active" xml:"is_active"`
	Name      string    `json:"name" yaml:"name" xml:"name"`
	Scores    []int64   `json:"scores,omitempty" yaml:"scores" xml:"scores"`
	Tags      []string  `json:"tags,omitempty" yaml:"tags" xml:"tags"`
}

Advanced Pattern-Based Customization

Configuration:

package: "api"
root_name: "Response"

types:
  mappings:
    - pattern: ".*_id$"
      type: "int64"
      comment: "Database ID"

naming:
  field_mappings:
    "user_id": "UserID"
    "api_key": "APIKey"

json_tags:
  additional_tags: ["yaml"]
  custom_options:
    - pattern: ".*secret.*|.*password.*"
      options: "-"
      comment: "Sensitive data excluded from serialization"
    - pattern: ".*_count$"
      options: "omitempty,string"
  skip_fields:
    - "internal_debug_info"

Input JSON:

{
  "user_id": 123,
  "api_key": "sk-1234567890",
  "password_hash": "hashed_password",
  "view_count": 42,
  "internal_debug_info": "debug data"
}

Generated Go Code:

package api

type Response struct {
	APIKey       string `json:"api_key" yaml:"api_key"`
	PasswordHash string `json:"password_hash,-" yaml:"password_hash"` // Sensitive data excluded from serialization
	UserID       int64  `json:"user_id" yaml:"user_id"` // Database ID
	ViewCount    int64  `json:"view_count,omitempty,string" yaml:"view_count"`
	// Note: internal_debug_info field is completely excluded
}

Type Detection

GoTyper automatically detects appropriate Go types based on the JSON data:

  • Strings β†’ string
  • Numbers β†’ int64 (for integers) or float64 (for decimals)
  • Booleans β†’ bool
  • Null β†’ pointer types with omitempty tag
  • Objects β†’ custom struct types
  • Arrays β†’ slices of appropriate types
  • Enhanced Time Detection β†’ time.Time
  • UUIDs (e.g., 123e4567-e89b-12d3-a456-426614174000) β†’ string

Enhanced Time Format Detection

GoTyper includes comprehensive time format detection that recognizes various timestamp formats commonly found in real-world APIs and data sources:

ISO8601 and RFC3339 Formats:

  • 2023-01-15T14:30:00Z (RFC3339)
  • 2023-01-15T14:30:00.123456789Z (RFC3339 with nanoseconds)
  • 2023-01-15T14:30:00+05:30 (ISO8601 with timezone)
  • 20230115T143000Z (ISO8601 basic format)
  • 2023-W03-1T10:30:00Z (ISO8601 week date)
  • 2023-015T10:30:00Z (ISO8601 ordinal date)

Date-Only Formats:

  • 2023-01-15 (ISO date)
  • 2023.01.15 (dot-separated)
  • 20230115 (compact format)

US Date Formats:

  • 01/15/2023 or 1/15/2023 (MM/DD/YYYY)
  • 01-15-2023 or 1-15-2023 (MM-DD-YYYY)

European Date Formats:

  • 15/01/2023 or 15/1/2023 (DD/MM/YYYY)
  • 15-01-2023 or 15-1-2023 (DD-MM-YYYY)
  • 15.01.2023 or 15.1.2023 (DD.MM.YYYY)

Time-Only Formats:

  • 14:30:15 or 14:30 (24-hour format)
  • 2:30:15 PM or 2:30 pm (12-hour format with AM/PM)

Month Name Formats:

  • January 15, 2023 or Jan 15, 2023 (US style)
  • 15 January 2023 (European style)

Unix Timestamps:

  • Unix timestamps (seconds and milliseconds) are kept as int64 by default for flexibility
  • Use unix_timestamps_as_time: true in configuration to convert them to time.Time

DateTime with Space:

  • 2023-01-15 14:30:00 (space-separated date and time)

Special Type Handling

Arrays and Slices

When processing JSON arrays, GoTyper analyzes the elements to determine the most appropriate slice type:

  • Arrays of primitives (strings, numbers, booleans) β†’ slices of the corresponding Go type
  • Arrays of objects β†’ slices of a custom struct type
  • Empty arrays β†’ []interface{} with omitempty tag
  • Mixed-type arrays β†’ []interface{}

Null Values

Fields that contain null in the JSON are converted to pointer types with the omitempty JSON tag. For example:

{
  "name": "John",
  "address": null
}

Becomes:

type RootType struct {
  Address *Address `json:"address,omitempty"`
  Name    string   `json:"name"`
}

Error Handling

GoTyper provides clear error messages for common issues:

  • Empty input: "empty input received"
  • Invalid JSON syntax: detailed parsing errors with position information
  • File not found: "failed to open file ''"
  • Multiple JSON values at root: "multiple JSON values at root level not supported"
  • Permission issues: "failed to write to file ''"

When an error occurs, GoTyper will display a user-friendly message and exit with a non-zero status code.

Configuration Reference

Complete Configuration Options

# Basic settings
package: "models"                    # Go package name
root_name: "APIResponse"            # Name for root struct

# Code formatting
formatting:
  enabled: true                     # Enable gofmt formatting
  use_gofumpt: false               # Use gofumpt instead of gofmt

# Type inference and mapping
types:
  force_int64: false               # Force all integers to int64
  optional_as_pointers: true       # Make nullable fields pointers
  unix_timestamps_as_time: false   # Convert Unix timestamps to time.Time instead of int64
  mappings:
    - pattern: ".*_id$|^id$"       # Regex pattern for field names
      type: "int64"                # Target Go type
      import: ""                   # Additional import if needed
      comment: "Database ID"       # Comment for generated field

# Field naming conventions
naming:
  pascal_case_fields: true         # Convert snake_case to PascalCase
  field_mappings:                  # Custom field name mappings
    "user_id": "UserID"
    "api_key": "APIKey"

# JSON tag generation
json_tags:
  omitempty_for_pointers: true     # Add omitempty to pointer fields
  omitempty_for_slices: true       # Add omitempty to slice fields
  additional_tags:                 # Additional tag formats to generate
    - "yaml"
    - "xml"
  custom_options:                  # Pattern-based tag customization
    - pattern: "password.*"        # Field pattern
      options: "-"                 # Tag options (-, omitempty, string, etc.)
      comment: "Excluded field"    # Comment for field
  skip_fields:                     # Fields to exclude entirely
    - "internal_use_only"
    - "debug_info"

# Validation tags (if implemented)
validation:
  enabled: false                   # Enable validation tag generation
  rules:
    - pattern: ".*email.*"
      tag: "validate:\"required,email\""

# Output options
output:
  file_header: ""                  # Custom file header
  generate_constructors: false    # Generate constructor functions
  generate_string_methods: false  # Generate String() methods

# Array handling
arrays:
  merge_different_objects: true   # Merge objects with different fields
  singularize_names: true         # Singularize array element struct names

# Development options
dev:
  debug: false                    # Enable debug output
  verbose: false                  # Enable verbose logging

Advanced Usage

Multi-Format Struct Generation

Generate structs that work with multiple serialization formats:

# Create a config file for multi-format output
cat > .gotyper.yml << EOF
json_tags:
  additional_tags:
    - "yaml"
    - "xml"
    - "toml"
EOF

# Generate structs with multiple tag formats
curl -s https://api.example.com/data | gotyper -p models

API-Specific Configurations

Create project-specific configurations for different APIs:

# GitHub API configuration
cat > github.gotyper.yml << EOF
package: "github"
root_name: "Repository"
types:
  mappings:
    - pattern: ".*_at$"
      type: "time.Time"
      import: "time"
naming:
  field_mappings:
    "html_url": "HTMLURL"
    "ssh_url": "SSHURL"
json_tags:
  custom_options:
    - pattern: ".*token.*"
      options: "-"
      comment: "Sensitive - excluded from JSON"
EOF

# Use specific config
gotyper -i github_response.json -c github.gotyper.yml

Security-Focused Configuration

Automatically handle sensitive fields:

# security-focused.gotyper.yml
json_tags:
  custom_options:
    # Exclude all password/secret/token fields
    - pattern: ".*password.*|.*secret.*|.*token.*|.*key$"
      options: "-"
      comment: "Sensitive data - excluded from JSON serialization"
    
    # Mark PII fields for careful handling
    - pattern: ".*email.*|.*phone.*|.*ssn.*"
      options: "omitempty"
      comment: "PII - handle with care"
  
  skip_fields:
    - "internal_id"
    - "debug_trace"
    - "raw_sql"

Development Workflow Integration

1. API Exploration Workflow

# Quick API exploration
curl -s https://api.example.com/users/123 | gotyper -I

2. Project Integration

# Add to your project
gotyper -i api_response.json -o internal/models/user.go -p models -r User

# With project-specific config
gotyper -i api_response.json -o models/api.go -c .gotyper.yml

3. CI/CD Integration

# Validate generated code compiles
gotyper -i schema.json -o /tmp/test.go && go build /tmp/test.go

# Generate and format in one step
gotyper -i data.json | gofmt > models/generated.go

License

MIT

About

Create Go structs from JSON πŸ”€

Resources

License

Contributing

Stars

Watchers

Forks

Contributors 2

  •  
  •