Skip to content
/ ft Public

Flexible types for un-marshalling any JSON value

License

Notifications You must be signed in to change notification settings

mozey/ft

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mozey/ft

Flexible types[1] for un-marshaling any JSON[2] value.

Why?

This lib is useful when API clients send inconsistent JSON.

For example, a webhook endpoint might want to accept POST requests with body:

  • This is what the API expects
{
    "sku": "123",
    "price": 9.99
}
  • This will also un-marshal, sku is cast to a string, and price to a float64. Errors if price is not a valid number
{
    "sku": 123,
    "price": "9.99"
}

Validation can then be done after un-marshaling

Usage

This package builds on

Numeric types (Int, Float) wrap 64bit base types, e.g. int64 and float64.

For each basic type, this package implements two flexible types[3], for example

  • required struct fields may use ft.String, it supports flexible types when un-marshaling.
  • optional fields may use ft.NString, it supports both flexible types and allows NULL. Think of the N-prefix as meaning "allows null", or "not-required"
type Data struct {
    Required ft.String `json:"required"`
    Optional ft.NString `json:"optional"`
}

The data type above can be used to un-marshal the following JSON

{
    "required": 123,
    "optional": null
}

NULL is not considered valid, for example

b := []byte(`{"required": 123,"optional": null}`)
d := Data{}
err := json.Unmarshal(b, &d)
// ft.String doesn't have the Valid property
fmt.Println(d.Required.String) // "123"
fmt.Println(d.Optional.Valid)  // false
fmt.Println(d.Optional.String) // ""

Valid is set if the value is defined (not null), don't use it for e.g. form validation. Rather create a method on the data type

// Valid validates all the required properties in the Data type
func (d *Data) Valid() bool {
    return d.Required.String == "123"
}

Flexible types can be used with the templating packages. The data type above could be used like this

t1 := template.Must(template.New("t1").Parse(
    `{{if not .Optional.Valid }}Required = {{.Required.String}} {{end}}`))
t1.Execute(os.Stdout, d) // Required = 123

Tests

See tests for more usage examples

git clone https://github.com/mozey/ft.git && cd ft
go clean -testcache && go test -v ./...

References

[1]

Not quite the same concept as Flexible typing in SQLite. Note that "as of SQLite version 3.37.0 (2021-11-27), SQLite supports this development style [Rigid Type Enforcement] using STRICT tables"

Previously this repo used the term "fuzzy types". It was renamed, and the term replaced with "flexible types". To avoid confusion with fuzzing, a new feature added to the "standard toolchain beginning in Go 1.18"

[2]

JSON spec

[3]

This repo does not use generics, and will compile with older versions of Go

About

Flexible types for un-marshalling any JSON value

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages