README
¶
go-jsonstruct
Generate Go structs from multiple JSON objects.
- What does go-jsonstruct do and why should I use it?
- How do I use go-jsonstruct?
- What are go-jsonstruct's key features?
- How does go-jsonstruct work?
- License
What does go-jsonstruct do and why should I use it?
go-jsonstruct generates Go structs from multiple JSON objects. Existing Go struct generators such as json-to-go and json2struct take only a single JSON object as input. go-jsonstruct takes multiple JSON objects as input and generates the most specific Go struct possible into which all the input objects can be unmarshalled.
This is useful if you have a collection of JSON objects, where no single object has all properties present, and you want to unmarshal those JSON objects into a Go program. Example collections include:
- JSON responses received from a REST API with no defined schema.
- Multiple values from a JSON column in an SQL database.
- All the JSON documents in a document database.
How do I use go-jsonstruct?
Install go-jsonstruct:
go install github.com/twpayne/go-jsonstruct/cmd/gojsonstruct@latest
Feed it some JSON objects. For example you can feed it with
{
"age": 37,
"user_height_m": 2
}
{
"age": 38,
"user_height_m": 1.7,
"favoriteFoods": [
"cake"
]
}
by running
echo '{"age":37,"user_height_m":2}' '{"age":38,"user_height_m":1.7,"favoriteFoods":["cake"]}' | gojsonstruct
This will output:
package main
type T struct {
Age int `json:"age"`
FavoriteFoods []string `json:"favoriteFoods,omitempty"`
UserHeightM float64 `json:"user_height_m"`
}
This example demonstrates:
age
is always observed as an integer, and so is given typeint
. The lower-case JSON propertyage
is converted into an exported Go field nameAge
for compatibility withencoding/json
.favoriteFoods
is observed as a JSON array, but is not always present, but when it is present it only contains JSON strings, and so is given type[]string
. ThecamelCase
namefavoriteFoods
is converted into the exported Go field nameFavoriteFoods
and is tagged withomitempty
.user_height_m
is observed as JSON numbers2
and1.7
, for which the most general Go type isfloat64
. Thesnake_case
nameuser_height_m
is converted to the exported Go field nameUserHeightM
.- Properties are sorted alphabetically.
go-jsonstruct recursively handles nested array elements and objects. For example, given the following three JSON objects input:
{
"nested": {
"bar": true,
"foo": "baz"
}
}
{
"nested": {
"bar": false,
"foo": null
}
}
{
"nested": {
"bar": true,
"foo": ""
}
}
which you can try by running
echo '{"nested":{"bar":true,"foo":"baz"}}' '{"nested":{"bar":false,"foo":null}}' '{"nested":{"bar":true,"foo":""}}' | gojsonstruct -packagename mypackage -typename MyType
generates the output
package mypackage
type MyType struct {
Nested struct {
Bar bool `json:"bar"`
Foo *string `json:"foo"`
} `json:"nested"`
}
This demonstrates:
- The package name and type name can be set on the command line.
- The arbitrarily-complex property
nested
is recursively converted to a nestedstruct
that all values can be unmarshalled to. go-jsonstruct handles array elements in an identical fashion, resolving array elements to the most-specific type. nested.bar
is always observed as a JSON bool, and is converted to a field of typebool
.nested.foo
is observed as a JSON string, a JSON null, and an empty JSON string and is converted to a field of type*string
withoutomitempty
. Withomitempty
, Go'sencoding/json
omits the field in the two cases ofnil
and a pointer to an emptystring
, so there is no way to distinguish between the observed values ofnull
and""
. go-jsonstruct falls back to the option of*string
withoutomitempty
which means that a value is always present, even if empty.
You can feed it your own data via the standard input, for example if you have a
file with one JSON object per line in objects.json
you can run:
gojsonstruct < objects.json
To learn about more about the available options, run:
gojsonstruct -help
What are go-jsonstruct's key features?
- Finds the most specific Go type that can represent all input values.
- Generates Go struct field names from
camelCase
,kebab-case
, andsnake_case
JSON object property names. - Capitalizes common abbreviations (e.g. HTTP, ID, and URL) when generating Go struct field names to follow Go conventions, with the option to add your own abbreviations.
- Gives you control over the output, including the generated package name, type name, and godoc-compatible comments.
- Generates deterministic output based only on the determined structure of the input, making it suitable for incorporation into build pipelines or detecting schema changes.
- Generates
omitempty
when possible. - Uses the standard library's
time.Time
when possible. - Gracefully handles properties with spaces that cannot be unmarshalled by
encoding/json
.
How does go-jsonstruct work?
go-jsonstruct consists of two phases: observation and code generation.
Firstly, in the observation phase, go-jsonstruct explores all the input objects and records statistics on what types are observed in each part. It recurses into objects and iterates over arrays.
Secondly, in the code generation phase, go-jsonstruct inspects the gathered
statistics and determines the strictest possible Go type that can represent all
the observed values. For example, the values 0
and 1
can be represented as
an int
, whereas the values 0
, 1
, and 2.2
require a float64
.
License
BSD
Documentation
¶
Index ¶
- Variables
- func SplitComponents(name string) []string
- type AbbreviationHandlingFieldNamer
- type FieldNamer
- type Generator
- type GeneratorOption
- func WithAddStructTagName(structTagName string) GeneratorOption
- func WithFieldNamer(fieldNamer FieldNamer) GeneratorOption
- func WithGoFormat(goFormat bool) GeneratorOption
- func WithImports(imports ...string) GeneratorOption
- func WithIntType(intType string) GeneratorOption
- func WithOmitEmpty(omitEmptyOption OmitEmptyOption) GeneratorOption
- func WithPackageComment(packageComment string) GeneratorOption
- func WithPackageName(packageName string) GeneratorOption
- func WithSkipUnparseableProperties(skipUnparseableProperties bool) GeneratorOption
- func WithStructTagName(structTagName string) GeneratorOption
- func WithStructTagNames(structTagNames []string) GeneratorOption
- func WithTypeComment(typeComment string) GeneratorOption
- func WithTypeName(typeName string) GeneratorOption
- func WithUseJSONNumber(useJSONNumber bool) GeneratorOption
- type ObservedValue
- type OmitEmptyOption
Constants ¶
This section is empty.
Variables ¶
Functions ¶
func SplitComponents ¶
SplitComponents splits name into components. name may be kebab case, snake case, or camel case.
Types ¶
type AbbreviationHandlingFieldNamer ¶
An AbbreviationHandlingFieldNamer generates Go field names from JSON properties while keeping abbreviations uppercased.
func (*AbbreviationHandlingFieldNamer) FieldName ¶
func (a *AbbreviationHandlingFieldNamer) FieldName(property string) string
FieldName implements FieldNamer.FieldName.
type FieldNamer ¶
A FieldNamer generates a Go field name from a JSON property.
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
A Generator generates Go types from ObservedValues.
func NewGenerator ¶
func NewGenerator(options ...GeneratorOption) *Generator
NewGenerator returns a new Generator with options.
type GeneratorOption ¶
type GeneratorOption func(*Generator)
A GeneratorOption sets an option on a Generator.
func WithAddStructTagName ¶ added in v1.1.0
func WithAddStructTagName(structTagName string) GeneratorOption
WithAddStructTagName adds a struct tag name.
func WithFieldNamer ¶
func WithFieldNamer(fieldNamer FieldNamer) GeneratorOption
WithFieldNamer sets the fieldNamer.
func WithGoFormat ¶
func WithGoFormat(goFormat bool) GeneratorOption
WithGoFormat sets whether the output is should be formatted with go fmt.
func WithImports ¶ added in v1.2.0
func WithImports(imports ...string) GeneratorOption
WithImports adds custom package imports.
func WithIntType ¶
func WithIntType(intType string) GeneratorOption
WithIntType sets the integer type.
func WithOmitEmpty ¶
func WithOmitEmpty(omitEmptyOption OmitEmptyOption) GeneratorOption
WithOmitEmpty sets whether each field is tagged with omitempty.
func WithPackageComment ¶
func WithPackageComment(packageComment string) GeneratorOption
WithPackageComment sets the package comment.
func WithPackageName ¶
func WithPackageName(packageName string) GeneratorOption
WithPackageName sets the package name.
func WithSkipUnparseableProperties ¶
func WithSkipUnparseableProperties(skipUnparseableProperties bool) GeneratorOption
WithSkipUnparseableProperties sets whether unparseable properties should be skipped.
func WithStructTagName ¶
func WithStructTagName(structTagName string) GeneratorOption
WithStructTagName sets the struct tag name.
func WithStructTagNames ¶ added in v1.1.0
func WithStructTagNames(structTagNames []string) GeneratorOption
WithStructTagNames sets the struct tag names.
func WithTypeComment ¶
func WithTypeComment(typeComment string) GeneratorOption
WithTypeComment sets the type comment.
func WithTypeName ¶
func WithTypeName(typeName string) GeneratorOption
WithTypeName sets the type name.
func WithUseJSONNumber ¶
func WithUseJSONNumber(useJSONNumber bool) GeneratorOption
WithUseJSONNumber sets whether to use json.Number when both int and float64s are observed for the same property.
type ObservedValue ¶
type ObservedValue struct { Observations int Empty int Array int Bool int Float64 int Int int Null int Object int String int Time int // time.Time is an implicit more specific type than string. AllArrayElementValues *ObservedValue AllObjectPropertyValues *ObservedValue ObjectPropertyValue map[string]*ObservedValue }
An ObservedValue is an observed value.
func ObserveJSON ¶
func ObserveJSON(r io.Reader) (*ObservedValue, error)
ObserveJSON returns all JSON values observed in r.
func ObserveYAML ¶
func ObserveYAML(r io.Reader) (*ObservedValue, error)
ObserveYAML returns all YAML values observed in r.
func (*ObservedValue) Merge ¶
func (o *ObservedValue) Merge(value interface{}) *ObservedValue
Merge merges value into o.
type OmitEmptyOption ¶
type OmitEmptyOption int
An OmitEmptyOption is an option for handling omitempty.
const ( OmitEmptyNever OmitEmptyOption = iota OmitEmptyAlways OmitEmptyAuto )
omitempty options.