A single, platform-agnostic JSON codec for Go that optimizes WebAssembly binary size by using zero reflection. It relies on fmt.Fielder for struct encoding/decoding, which is typically generated by ormc.
- Zero Reflection: Uses type switches and
fmt.Fielderinstead of thereflectpackage. - Platform-Agnostic: Identical behavior on all platforms (WASM, Linux, macOS, etc.).
- TinyGo Compatible: Optimized for minimal binary size and memory usage.
- Fielder-Only: Only types implementing
fmt.Fieldercan be directly encoded or decoded.
Structs MUST implement fmt.Fielder to be supported. This is normally handled by generating code with ormc.
package main
import (
"github.com/tinywasm/fmt"
"github.com/tinywasm/json"
)
// User implements fmt.Fielder (typically via ormc)
type User struct {
Name string
}
func (u *User) Schema() []fmt.Field {
return []fmt.Field{{Name: "name", Type: fmt.FieldText}}
}
func (u *User) Pointers() []any { return []any{&u.Name} }
func main() {
u := User{Name: "Alice"}
var out string
if err := json.Encode(&u, &out); err != nil {
panic(err)
}
// out: {"name":"Alice"}
var result User
if err := json.Decode(out, &result); err != nil {
panic(err)
}
// Recommended: Explicit validation (if result implements fmt.Validator or uses fmt.ValidateFields)
// if err := fmt.ValidateFields('c', &result); err != nil {
// panic(err)
// }
}Serializes a Fielder to JSON. JSON keys are always taken from field.Name. If field.OmitEmpty is true, the field is skipped if its value is zero.
- data: Must implement
fmt.Fielder. - output:
*[]byte,*string, orio.Writer.
Parses JSON into a Fielder.
- input:
[]byte,string, orio.Reader. - data: Must implement
fmt.Fielder.
To maintain a minimal footprint and zero reflection, tinywasm/json has specific support and constraints:
The encoder and decoder directly support the following fmt.FieldType mappings:
| Go Type | fmt.FieldType |
JSON Equivalent |
|---|---|---|
string |
FieldText |
string |
int, int64, etc. |
FieldInt |
number |
float64, float32 |
FieldFloat |
number |
bool |
FieldBool |
boolean |
[]byte |
FieldBlob |
string (escaped) |
[]int |
FieldIntSlice |
array of numbers |
Fielder |
FieldStruct |
object (nested) |
- No Reflection: Generic types like
map[string]any,[]any, or arbitrary structs NOT implementingfmt.Fielderare NOT supported. - Root Object Only: Both
EncodeandDecodeexpect afmt.Fielderas the root element. You cannot directly encode/decode a bare string or number as a standalone JSON value. - No Maps: Key-value pairs are only supported via struct fields described in the
Schema(). - Simplified Arrays: Currently, only
[]intis supported as a slice type. Other slice types like[]stringor[]float64are not yet supported. - No Custom Marshaling: Standard interfaces like
json.Marshalerorjson.Unmarshalerare ignored. - Fielder Contract: Structs must return pointers to all fields in the same order as the schema via
Pointers().
tinywasm/json is 77% smaller than encoding/json in WASM (~27 KB vs ~119 KB)
and zero-reflect, eliminating reflection overhead and heavy dependencies.
| Benchmark | tinywasm/json | encoding/json |
|---|---|---|
| Encode | 285 ns/op | 276 ns/op |
| Decode | 320 ns/op | 1078 ns/op |
See full results and analysis in benchmarks/README.md.
See LICENSE for details.