A simple code generator of JSON marshaler for go and tinygo.
Generated marshaler has fewer runtime dependencies (i.e. it doesn't depend on encoding/json
and reflection) and it's fast.
tinygo doesn't support encoding/json
package because reflection support is not enough: https://tinygo.org/lang-support/stdlib/#encoding-json
This library aims to marshal a struct to JSON on tinygo environment. And it also supposes to run this library in restricted environment; for example, wasm sandbox runtime.
So this library has the following features:
- don't depend on
encoding/json
package - don't depend on
js
package- i.e. don't depend on browser wasm runtime
This library has only one dependency for now, strconv
.
encoding/json
uses runtime reflection to marshal and unmarshal, but this library doesn't depend on it. Basically, code generation a marshaller statically by without reflection makes the performance be better.
go generate
with the following struct that uses json-ice
,
//go:generate json-ice --type=AwesomeStruct
type AwesomeStruct struct {
Foo string `json:"foo"`
Bar string `json:"bar,omitempty"`
}
then it generates marshaler code as MarshalAwesomeStructAsJSON(s *AwesomeStruct) ([]byte, error)
; you can use that like the following:
marshaled, err := MarshalAwesomeStructAsJSON(&AwesomeStruct{
Foo: "buz",
Bar: "",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", marshaled) // => {"foo":"buz"}
json-ice:
-cap-size int
[optional] a cap-size of a byte slice buffer for marshaling; by default, it calculates this value automatically
-output string
[optional] output file name (default "srcdir/<type>_gen.go")
-toplevel-array
[optional] generate a marshaler for toplevel-array with given type by "--type" (e.g. "[]string")
-toplevel-map
[optional] generate a marshaler for toplevel-array with given type by "--type" (e.g. "[]string")
-type string
[mandatory] a type name
-version
show version information
$ go get -u github.com/moznion/go-json-ice/cmd/json-ice
also you can get the pre-built binaries on Releases.
Parses given struct and generates a marshaler according to the parsed result; it means resolve fields statically without runtime reflection.
It calculates a cap-size of a byte slice buffer for marshaling automatically, but you can specify it by yourself.
If you need more throughput, it is a good idea to specify it as a bigger cap-size, on the other hand, if you would like to save the amount of memory, you can put it as a smaller (e.g. 1
) cap-size.
=== auto capsize ===
goos: darwin
goarch: amd64
pkg: github.com/moznion/go-json-ice/benchmark
BenchmarkMarshal_EncodingJSON-12 1179334 1010 ns/op 320 B/op 1 allocs/op
BenchmarkMarshal_JSONIce-12 2014201 629 ns/op 384 B/op 1 allocs/op
PASS
ok github.com/moznion/go-json-ice/benchmark 4.099s
=== minimum capsize => 1 ===
goos: darwin
goarch: amd64
pkg: github.com/moznion/go-json-ice/benchmark
BenchmarkMarshal_EncodingJSON-12 1136454 1003 ns/op 320 B/op 1 allocs/op
BenchmarkMarshal_JSONIce-12 1421254 846 ns/op 1009 B/op 7 allocs/op
PASS
ok github.com/moznion/go-json-ice/benchmark 4.287s
auto capsize
benchmarks it the default situation, and minimum capsize
benchmarks it with the minimum cap-size (i.e. in worst performance case: 1
).
make bench
command benchmarks the performance.
No, this only supports marshaling. If you are looking for a way to marshal JSON with tinygo, please consider using buger/jsonparser.
For example,
type Foo struct {
Hoge string `json:"hoge"`
}
//go:generate json-ice --type=Bar
type Bar struct {
Foo *Foo `json:"foo"`
}
In this case, a marshaler for Bar
tries to marshal Foo
by Foo.MarshalJSON() ([]byte, error)
. It means it expects the target type implements json.Marshaler
when it tries to marshal a non-primitive type.
The easiest way to marshal the struct like the above is the follwoing:
//go:generate json-ice --type=Foo
type Foo struct {
Hoge string `json:"hoge"`
}
//go:generate json-ice --type=Bar
type Bar struct {
Foo *Foo `json:"foo"`
}
func (f *Foo) MarshalJSON() ([]byte, error) {
return MarshalFooAsJSON(f)
}
It is necessary to generate marshaler code by like the following instructions:
//go:generate json-ice --type=[]string --toplevel-array
- then, it generates a marshaler for toplevel array
[]string
asMarshalStringArrayAsJSON(st []string) ([]byte, error)
- then, it generates a marshaler for toplevel array
//go:generate json-ice --type=map[string]string --toplevel-map
- then, it generates a marshaler for toplevel map
map[string]string
asMarshalStringToStringMapAsJSON(mt map[string]string) ([]byte, error)
- then, it generates a marshaler for toplevel map
- it cannot marshal
interface{}
values- because it doesn't use reflection so it cannot resolve the actual type dynamically
- in other words, the target type has to be resolvable as statically
- it cannot marshal
named type
andtype alias
types automatically- if you would like to marshal such them, please implement
json.Marshaler
on the type as like as the above FAQ answer.
- if you would like to marshal such them, please implement
Binaries are built and uploaded by goreleaser. Please refer to the configuration file: .goreleaser.yml
moznion (moznion@gmail.com)