dirty

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2026 License: MIT Imports: 3 Imported by: 0

README

d3rty/json

Gracefully handling schema variations.

Flexible JSON unmarshalling for Go, forgiving of dirty real-world data.

Go Reference CI Go Version


d3rty/json parses JSON from sources you can't fully control or trust: third-party APIs with schema flakiness, legacy feeds, AI-generated output. Stringified numbers, "on"/"off" booleans, drifting key casing - instead of failing on the first mismatch, it decodes strictly first and falls back to a forgiving dirty schema you define per model.

[!NOTE] Work in progress: the API may still change before v1.

data := []byte(`{"id": "123"}`)

var plain struct{ ID int `json:"id"` }
json.Unmarshal(data, &plain)  // error: cannot unmarshal string into Go struct field .id of type int

var event Event               // same shape, dirty-enabled (see Quick Start)
dirty.Unmarshal(data, &event) // nil; event.ID == 123

Install

go get github.com/d3rty/json
import dirty "github.com/d3rty/json"

Quick Start

Embed dirty.Enabled into your clean model and describe what may be dirty in a Dirty() counterpart. Everything else stays strict.

package main

import (
	"fmt"
	"log"

	dirty "github.com/d3rty/json"
)

// Event is a regular "clean" model with strict types.
type Event struct {
	dirty.Enabled // opts this model into dirty unmarshalling

	Name string `json:"name"`

	// Covered by the dirty schema below: stringified numbers,
	// sloppy booleans, etc. are forgiven.
	ID       int  `json:"id"`
	IsActive bool `json:"is_active"`

	// Not covered by the dirty schema: must be a real bool or it's lost.
	MustBool bool `json:"must_bool"`
}

// Dirty links the clean model to its dirty counterpart.
func (e *Event) Dirty() any {
	return &struct {
		ID       dirty.Number `json:"id"`
		IsActive dirty.Bool   `json:"is_active"`
	}{}
}

func main() {
	data := []byte(`{"id": "123", "name": "Sample Event", "is_active": "on", "must_bool": true}`)

	var event Event
	if err := dirty.Unmarshal(data, &event); err != nil {
		log.Fatal(err)
	}

	fmt.Println(event.ID)       // 123
	fmt.Println(event.IsActive) // true
	fmt.Println(event.MustBool) // true
}

The dirty types are dirty.Number, dirty.String, dirty.Bool, dirty.Array and dirty.Object. Swap dirty.Enabled for dirty.Disabled to switch a model back to strict stdlib behavior without touching anything else.

Flexible Keys

Key naming drifts too: isActive today, IsActive tomorrow, is-active from the mobile team. The FlexKeys option (off by default) matches keys case-insensitively and across camelCase/snake_case/kebab-case/PascalCase:

dirty.ConfigSetGlobal(func(cfg *dirty.Config) {
	cfg.FlexKeys.Disabled = false
	cfg.FlexKeys.CaseInsensitive = true
	cfg.FlexKeys.ChameleonCase = true
})

// key arrives as PascalCase, value as a string - both forgiven
data := []byte(`{"IsActive": "on"}`)

The case-detection machinery is exposed as its own package, cases.

Configuration

Every forgiveness rule is a switch in a TOML config: which strings count as true, whether "1 000 000" or "1e6" are numbers, which date layouts to try, and so on. The full annotated list of defaults lives in default.toml.

// tweak the global config in place
dirty.ConfigSetGlobal(func(cfg *dirty.Config) {
	cfg.Bool.FromStrings.CustomListForTrue = []string{"true", "yes", "on", "da"}
})

// or load a full config from TOML bytes
cfg := dirty.ConfigFromBytes(tomlBytes)

Unmarshalling results are classified by color: 🟢 green (decoded cleanly), 🟡 yellow (decoded via forgiving conversions), 🔴 red (data was lost). Reporting this back to the caller is not implemented yet.

Playground

The WASM demo runs the real decoder in your browser: paste dirty JSON, toggle config switches, see what gets forgiven.

Feedback

d3rty/json is a solo, opinionated project - but if you stumbled upon it and have ideas, questions, or bug reports, an issue is always welcome :)

License

MIT © d3rty

Documentation

Overview

Package dirty provides clean unmarshalling of dirty JSON data. Dirty JSON data is JSON with unstable schema, flaky keys, etc.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ConfigSetGlobal allows us to update the global config.
	ConfigSetGlobal = config.SetGlobal

	// ConfigFromBytes parses a TOML configuration from bytes.
	ConfigFromBytes = config.FromBytes
)

Functions

func Unmarshal

func Unmarshal(data []byte, v any) error

Unmarshal parses the JSON-encoded data, allowing schema to be dirty, and stores the result in the value pointed to by v. It's the main part of public API, it's considered to be used instead of json.Unmarshal.

Types

type Array

type Array = dirtyjson.Array

Array is a custom type for unmarshalling arrays. Anything except actual JSON arrays will be rejected.

type Bool

type Bool = dirtyjson.Bool

Bool is a custom type for unmarshalling booleans. Bools can be parsed from

  • strings ("true", "false", "yes", "no", "on", "off", "1", "0")
  • numbers (1, 0)
  • actual JSON booleans.

Other types will be rejected.

type Config

type Config = config.Config

type Disabled

type Disabled = dirtyjson.Disabled

Disabled is an atom struct that that remains a syntactically valid dirty model but disables dirty unmarshalling. You can easily switch from `dirty.Enabled` to `dirty.Disabled` keeping all models and interfaces working (falling back to standard (clean) json.Unmarshal).

type Enabled

type Enabled = dirtyjson.Enabled

Enabled is an atom struct that enables dirty unmarshalling for a given clean model. It MUST be embedded into any clean model. The clean model also MUST implement Dirtyable interface.

type Number

type Number = dirtyjson.Number

Number is a custom type for unmarshalling numbers. Numbers can be parsed from strings or actual JSON numbers. Other types will be rejected.

type Object

type Object = dirtyjson.Object

Object is a custom type for unmarshalling objects. Anything except actual JSON objects will be rejected.

type String

type String = dirtyjson.String

String s a custom type for unmarshalling strings. Anything except an actual JSON string will be rejected.

Directories

Path Synopsis
cmd
config-schema command
wasm-demo command
internal
tests

Jump to

Keyboard shortcuts

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