json

package
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 21, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

README

JSON Naming Convention Wrapper

This package provides JSON marshal/unmarshal functions that use custom naming conventions (kebab-case or snake_case) for struct fields by default, while respecting explicit json: tags.

Features

  • Automatic kebab-case or snake_case conversion: Struct fields without explicit json: tags are automatically converted
  • Tag precedence: Explicit json: tags are always respected
  • Recursive transformation: Works with nested structs, slices, and maps
  • Indent variants: Support for both compact and indented JSON output
  • Thread-safe: Safe for concurrent use (no mutable state)
  • File I/O helpers: Convenience functions for reading/writing JSON files
  • Standard behavior: Inline with encoding/json (supports omitempty, string tags, etc.)

Installation

go get codeberg.org/basvanbeek/run/pkg/json

Usage

Using Kebab-case
package main

import (
    "fmt"
    "codeberg.org/basvanbeek/run/pkg/json"
)

type Config struct {
    ServerName string  // Will be marshaled as "server-name"
    ListenPort int     // Will be marshaled as "listen-port"
    EnableTLS  bool    // Will be marshaled as "enable-tls"
}

func main() {
    cfg := Config{
        ServerName: "my-server",
        ListenPort: 8080,
        EnableTLS:  true,
    }

    // Marshal to JSON with kebab-case (compact)
    data, err := json.MarshalKebab(cfg)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
    // Output: {"server-name":"my-server","listen-port":8080,"enable-tls":true}

    // Marshal with indentation
    data, err = json.MarshalKebabIndent(cfg, "", "  ")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
    // Output:
    // {
    //   "server-name": "my-server",
    //   "listen-port": 8080,
    //   "enable-tls": true
    // }

    // Unmarshal from JSON with kebab-case
    var loaded Config
    err = json.UnmarshalKebab(data, &loaded)
    if err != nil {
        panic(err)
    }
}
Using Snake_case

The same functionality is available with snake_case naming:

package main

import (
    "fmt"
    "codeberg.org/basvanbeek/run/pkg/json"
)

type Config struct {
    ServerName string  // Will be marshaled as "server_name"
    ListenPort int     // Will be marshaled as "listen_port"
    EnableTLS  bool    // Will be marshaled as "enable_tls"
}

func main() {
    cfg := Config{
        ServerName: "my-server",
        ListenPort: 8080,
        EnableTLS:  true,
    }

    // Marshal to JSON with snake_case (compact)
    data, err := json.MarshalSnake(cfg)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
    // Output: {"server_name":"my-server","listen_port":8080,"enable_tls":true}

    // Marshal with indentation
    data, err = json.MarshalSnakeIndent(cfg, "", "  ")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
    // Output:
    // {
    //   "server_name": "my-server",
    //   "listen_port": 8080,
    //   "enable_tls": true
    // }

    // Unmarshal from JSON with snake_case
    var loaded Config
    err = json.UnmarshalSnake(data, &loaded)
    if err != nil {
        panic(err)
    }
}
Using Explicit Tags

Fields with explicit json: tags will use the tag value instead of automatic conversion:

type Config struct {
    ServerName string `json:"custom_server_name"`  // Uses "custom_server_name"
    ListenPort int                                  // Uses "listen-port" (kebab) or "listen_port" (snake)
    EnableTLS  bool   `json:"tls_enabled"`         // Uses "tls_enabled"
}
Nested Structs

The wrapper recursively transforms nested structs:

type AppConfig struct {
    ServerSettings ServerSettings  // Will be "server-settings" or "server_settings"
    DatabaseConfig DatabaseConfig  // Will be "database-config" or "database_config"
}

type ServerSettings struct {
    HostName   string  // Will be "host-name" or "host_name"
    ListenPort int     // Will be "listen-port" or "listen_port"
}

type DatabaseConfig struct {
    ConnectionString string           // Will be "connection-string" or "connection_string"
    MaxConnections   int `json:"max_conn"`  // Uses explicit tag "max_conn"
}
File I/O

File operations automatically use indented output (2-space indent) for readability:

// Write to file (indented)
cfg := Config{ServerName: "test", ListenPort: 8080}
err := json.MarshalKebabToFile("config.json", cfg)

// Read from file
var loaded Config
err = json.UnmarshalKebabFromFile("config.json", &loaded)

// Snake_case variants
err = json.MarshalSnakeToFile("config.json", cfg)
err = json.UnmarshalSnakeFromFile("config.json", &loaded)
Embedded Structs

Exported embedded struct fields are processed normally:

type BaseConfig struct {
    ServerName string
}

type AppConfig struct {
    BaseConfig  // Fields will be inlined
    ExtraField  string
}
Ignoring Fields

Use json:"-" to ignore fields:

type Config struct {
    VisibleField string
    IgnoredField string `json:"-"`  // Will not be marshaled
}

API Reference

Kebab-case Functions
  • MarshalKebab(v interface{}) ([]byte, error)
    Marshals v to compact JSON using kebab-case for field names without explicit tags.

  • MarshalKebabIndent(v interface{}, prefix, indent string) ([]byte, error)
    Marshals v to indented JSON using kebab-case for field names without explicit tags.

  • UnmarshalKebab(data []byte, v interface{}) error
    Unmarshals JSON data into v, expecting kebab-case field names for fields without explicit tags.

  • MarshalKebabToFile(filename string, v interface{}) error
    Marshals v to a JSON file using kebab-case naming convention (always indented with 2 spaces).

  • UnmarshalKebabFromFile(filename string, v interface{}) error
    Unmarshals JSON from a file into v using kebab-case naming convention.

Snake_case Functions
  • MarshalSnake(v interface{}) ([]byte, error)
    Marshals v to compact JSON using snake_case for field names without explicit tags.

  • MarshalSnakeIndent(v interface{}, prefix, indent string) ([]byte, error)
    Marshals v to indented JSON using snake_case for field names without explicit tags.

  • UnmarshalSnake(data []byte, v interface{}) error
    Unmarshals JSON data into v, expecting snake_case field names for fields without explicit tags.

  • MarshalSnakeToFile(filename string, v interface{}) error
    Marshals v to a JSON file using snake_case naming convention (always indented with 2 spaces).

  • UnmarshalSnakeFromFile(filename string, v interface{}) error
    Unmarshals JSON from a file into v using snake_case naming convention.

Behavior

Naming Conventions

Kebab-case conversion:

  • ServerNameserver-name
  • ListenPortlisten-port
  • TLSEnabledtls-enabled
  • HTTPServerhttp-server
  • MaxHTTPConnectionsmax-http-connections

Snake_case conversion:

  • ServerNameserver_name
  • ListenPortlisten_port
  • TLSEnabledtls_enabled
  • HTTPServerhttp_server
  • MaxHTTPConnectionsmax_http_connections
Field Processing
  1. Unexported fields: Skipped (standard Go behavior)
  2. Exported fields without tags: Converted to kebab-case or snake_case (depending on which function is used)
  3. Exported fields with json: tags: Use the tag value
  4. Embedded structs: Inlined at the parent level (standard JSON behavior)
  5. Fields with json:"-": Ignored
JSON Tag Options

All standard encoding/json tag options are supported:

  • json:"name" - Custom field name
  • json:"name,omitempty" - Omit if empty
  • json:"name,string" - Marshal number/bool as string
  • json:"-" - Skip field
Type Support

The wrapper supports all standard Go types:

  • Basic types: string, int, bool, float64, etc.
  • Structs (including nested)
  • Slices and arrays
  • Maps
  • Pointers
Output Format
  • Compact functions (MarshalKebab, MarshalSnake): No whitespace, minimal size
  • Indent functions (MarshalKebabIndent, MarshalSnakeIndent): Custom indentation
  • File functions (MarshalKebabToFile, MarshalSnakeToFile): Always indented with 2 spaces for readability

Thread Safety

This package is thread-safe for concurrent marshal/unmarshal operations, as it uses no mutable state and relies on the thread-safe encoding/json library.

License

Copyright (c) 2026 Bas van Beek. Licensed under the Apache License Version 2.0.

Documentation

Overview

Package json provides JSON marshal/unmarshal functions with custom naming conventions.

Package json provides JSON marshal/unmarshal functions with custom naming conventions.

Package json provides JSON marshal/unmarshal functions with custom naming conventions.

Example

Example demonstrates basic usage of MarshalKebab and UnmarshalKebab.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string
		ListenPort int
		EnableTLS  bool
	}

	cfg := Config{
		ServerName: "my-server",
		ListenPort: 8080,
		EnableTLS:  true,
	}

	// Marshal to JSON with kebab-case
	data, err := json.MarshalKebab(cfg)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))

	// Unmarshal from JSON with kebab-case
	var loaded Config
	if err := json.UnmarshalKebab(data, &loaded); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Loaded: %+v\n", loaded)
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func MarshalKebab

func MarshalKebab(v interface{}) ([]byte, error)

MarshalKebab marshals v to JSON bytes using kebab-case for field names that don't have explicit json tags. Fields with explicit json:"name" tags will use the tag value. This produces compact JSON output.

This function uses reflection to transform struct field names to kebab-case before marshaling. Only exported fields are processed (standard Go behavior).

Example

ExampleMarshalKebab demonstrates marshaling with explicit tags.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string `json:"custom_server_name"`
		ListenPort int    // Will use kebab-case: listen-port
		EnableTLS  bool   `json:"tls_enabled"`
	}

	cfg := Config{
		ServerName: "test-server",
		ListenPort: 8080,
		EnableTLS:  true,
	}

	data, err := json.MarshalKebab(cfg)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
}

func MarshalKebabIndent

func MarshalKebabIndent(v interface{}, prefix, indent string) ([]byte, error)

MarshalKebabIndent marshals v to indented JSON bytes using kebab-case for field names that don't have explicit json tags. Each JSON element begins on a new line indented by prefix followed by one or more copies of indent according to the nesting depth.

Example

ExampleMarshalKebabIndent demonstrates marshaling with indentation.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string
		ListenPort int
	}

	cfg := Config{
		ServerName: "test-server",
		ListenPort: 8080,
	}

	data, err := json.MarshalKebabIndent(cfg, "", "  ")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
}

func MarshalKebabToFile

func MarshalKebabToFile(filename string, v interface{}) error

MarshalKebabToFile marshals v to a JSON file using kebab-case naming convention. The output is indented with 2 spaces for readability.

Example

ExampleMarshalKebabToFile demonstrates writing JSON to a file.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string
		ListenPort int
	}

	cfg := Config{
		ServerName: "file-server",
		ListenPort: 9090,
	}

	// Write to file (indented automatically)
	if err := json.MarshalKebabToFile("/tmp/config.json", cfg); err != nil {
		log.Fatal(err)
	}

	fmt.Println("Config written to /tmp/config.json")
}

func MarshalSnake

func MarshalSnake(v interface{}) ([]byte, error)

MarshalSnake marshals v to JSON bytes using snake_case for field names that don't have explicit json tags. Fields with explicit json:"name" tags will use the tag value. This produces compact JSON output.

This function uses reflection to transform struct field names to snake_case before marshaling. Only exported fields are processed (standard Go behavior).

Example

ExampleMarshalSnake demonstrates basic usage of MarshalSnake.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string
		ListenPort int
		EnableTLS  bool
	}

	cfg := Config{
		ServerName: "my-server",
		ListenPort: 8080,
		EnableTLS:  true,
	}

	data, err := json.MarshalSnake(cfg)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
}

func MarshalSnakeIndent

func MarshalSnakeIndent(v interface{}, prefix, indent string) ([]byte, error)

MarshalSnakeIndent marshals v to indented JSON bytes using snake_case for field names that don't have explicit json tags. Each JSON element begins on a new line indented by prefix followed by one or more copies of indent according to the nesting depth.

Example

ExampleMarshalSnakeIndent demonstrates marshaling snake_case with indentation.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string
		ListenPort int
	}

	cfg := Config{
		ServerName: "test-server",
		ListenPort: 8080,
	}

	data, err := json.MarshalSnakeIndent(cfg, "", "  ")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
}

func MarshalSnakeToFile

func MarshalSnakeToFile(filename string, v interface{}) error

MarshalSnakeToFile marshals v to a JSON file using snake_case naming convention. The output is indented with 2 spaces for readability.

Example

ExampleMarshalSnakeToFile demonstrates writing JSON with snake_case to a file.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string
		ListenPort int
	}

	cfg := Config{
		ServerName: "file-server",
		ListenPort: 9090,
	}

	// Write to file (indented automatically)
	if err := json.MarshalSnakeToFile("/tmp/config-snake.json", cfg); err != nil {
		log.Fatal(err)
	}

	fmt.Println("Config written to /tmp/config-snake.json")
}

func UnmarshalKebab

func UnmarshalKebab(data []byte, v interface{}) error

UnmarshalKebab unmarshals JSON bytes into v, expecting kebab-case field names for fields without explicit json tags. Fields with explicit json:"name" tags will use the tag value.

This function uses reflection to transform struct field names to kebab-case before unmarshaling. Only exported fields are processed (standard Go behavior).

Example

ExampleUnmarshalKebab demonstrates unmarshaling with nested structs.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type ServerSettings struct {
		HostName   string
		ListenPort int
	}

	type DatabaseConfig struct {
		ConnectionString string
		MaxConnections   int `json:"max_conn"`
	}

	type AppConfig struct {
		ServerSettings ServerSettings
		DatabaseConfig DatabaseConfig
	}

	jsonData := []byte(`{
		"server-settings": {
			"host-name": "localhost",
			"listen-port": 8080
		},
		"database-config": {
			"connection-string": "postgres://localhost",
			"max_conn": 10
		}
	}`)

	var cfg AppConfig
	if err := json.UnmarshalKebab(jsonData, &cfg); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Server: %s:%d\n", cfg.ServerSettings.HostName, cfg.ServerSettings.ListenPort)
	fmt.Printf("Database: %s (max %d connections)\n",
		cfg.DatabaseConfig.ConnectionString,
		cfg.DatabaseConfig.MaxConnections)
}

func UnmarshalKebabFromFile

func UnmarshalKebabFromFile(filename string, v interface{}) error

UnmarshalKebabFromFile unmarshals JSON from a file into v using kebab-case naming convention.

Example

ExampleUnmarshalKebabFromFile demonstrates reading JSON from a file.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string
		ListenPort int
	}

	var cfg Config
	if err := json.UnmarshalKebabFromFile("/tmp/config.json", &cfg); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Loaded config: %+v\n", cfg)
}

func UnmarshalSnake

func UnmarshalSnake(data []byte, v interface{}) error

UnmarshalSnake unmarshals JSON bytes into v, expecting snake_case field names for fields without explicit json tags. Fields with explicit json:"name" tags will use the tag value.

This function uses reflection to transform struct field names to snake_case before unmarshaling. Only exported fields are processed (standard Go behavior).

Example

ExampleUnmarshalSnake demonstrates unmarshaling with snake_case.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string
		ListenPort int
		EnableTLS  bool
	}

	jsonData := []byte(`{
		"server_name": "test-server",
		"listen_port": 8080,
		"enable_tls": true
	}`)

	var cfg Config
	if err := json.UnmarshalSnake(jsonData, &cfg); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Server: %s:%d (TLS: %v)\n", cfg.ServerName, cfg.ListenPort, cfg.EnableTLS)
}

func UnmarshalSnakeFromFile

func UnmarshalSnakeFromFile(filename string, v interface{}) error

UnmarshalSnakeFromFile unmarshals JSON from a file into v using snake_case naming convention.

Example

ExampleUnmarshalSnakeFromFile demonstrates reading JSON with snake_case from a file.

package main

import (
	"fmt"
	"log"

	"codeberg.org/basvanbeek/run/pkg/json"
)

func main() {
	type Config struct {
		ServerName string
		ListenPort int
	}

	var cfg Config
	if err := json.UnmarshalSnakeFromFile("/tmp/config-snake.json", &cfg); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Loaded config: %+v\n", cfg)
}

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL