https://github.com/akutz/gdj
Golang's "encoding/json" package with support for discriminators
https://github.com/akutz/gdj
discriminator encoding go golang json
Last synced: 2 months ago
JSON representation
Golang's "encoding/json" package with support for discriminators
- Host: GitHub
- URL: https://github.com/akutz/gdj
- Owner: akutz
- License: bsd-3-clause
- Created: 2022-12-15T16:55:34.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2023-09-24T18:58:55.000Z (about 2 years ago)
- Last Synced: 2025-07-31T20:04:33.458Z (2 months ago)
- Topics: discriminator, encoding, go, golang, json
- Language: Go
- Homepage:
- Size: 248 KB
- Stars: 1
- Watchers: 2
- Forks: 2
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Go's Discriminating JSON (gdj)
This repository contains a fork of Golang's [`encoding/json`](https://pkg.go.dev/encoding/json) package, enhanced with support for encoding type information into objects via a [type discriminator](https://www.rfc-editor.org/rfc/rfc8927#name-discriminator).
## Overview
This project enhances the Go package `encoding/json` with support for an optional, [type discriminator](https://www.rfc-editor.org/rfc/rfc8927#name-discriminator) when encoding/decoding values to/from JSON. For more information please see the associated, [JSON Discriminator Proposal](./proposal.md) that will be submitted to Go.
## Goals
This project is intended to help drive the following goals:
* Introduce support for a JSON discriminator in Go's `encoding/json` package
* Make it easy for people to use JSON discriminators _today_ with minimal changes to their existing types/code
* Support all versions of Go newer than or equal to 1.17.13, 1.18.9, 1.19.4, 1.20rc1## Getting started
Using this project is quite simple, just:
1. import `github.com/akutz/gdj`
1. create a new `json.Encoder` or `json.Decoder`
1. set the discriminator on the new encoder/decoderThe following example illustrates a encoding and decoding JSON with a discriminator:
```go
import "github.com/akutz/gdj" // imports as the "json" package// ...
type Person struct {
Name string `json:"name"`
Attributes []interface{} `json:"attributes,omitempty"`
}type Spouse struct {
Person
}enc := json.NewEncoder(os.Stdout)
enc.SetDiscriminator("type", "value", 0)enc.Encode(Person{"Andrew", []interface{}{"Austin", uint8(42)}})
enc.Encode(Person{"Mandy", []interface{}{Spouse{Person{"Andrew", nil}}}})
```The above program will emit the following output:
```
{"name":"Andrew","attributes":[{"type":"string","value":"Austin"},{"type":"uint8","value":42}]}
{"name":"Mandy","attributes":[{"type":"Spouse","name":"Andrew"}]}
```The type information is encoded alongside the values for the elements in the field `Person.Attributes`. It is also possible to decode the information back to a `Person` while maintaining the same type information:
```go
var jsonBlob = `{
"name":"Andrew",
"attributes":[
{"type":"string", "value": "Austin"},
{"type":"uint8", "value":42}
]
}
{
"name":"Mandy",
"attributes":[
{"type":"Spouse", "name": "Andrew"}
]
}`dec := json.NewDecoder(strings.NewReader(jsonBlob))
dec.SetDiscriminator("type", "value", func(s string) (reflect.Type, bool) {
switch s {
case "Person":
return reflect.TypeOf(Person{}), true
case "Spouse":
return reflect.TypeOf(Spouse{}), true
}
return nil, false
})var p Person
dec.Decode(&p)
fmt.Printf("%[1]T(%[1]d)\n", p.Attributes[1])dec.Decode(&p)
fmt.Printf("%[1]T(%[1]s)\n", p.Attributes[0].(Spouse).Name)
```The above program emits the following:
```
uint8(42)
string(Andrew)
```The output indicates the original type and value information was respected when the data was decoded. For more examples, please see:
* [`discriminator_test.go`](./discriminator_test.go)
* [`example_discriminator_test.go`](./example_discriminator_test.go)
* [`govmomi_test.go`](./canaries/govmomi_test.go)## Type support
The discriminator supports encoding and decoding the following, built-in types:
* `uint`
* `uint8`
* `uint16`
* `uint32`
* `uint64`
* `uintptr`
* `int`
* `int8`
* `int16`
* `int32`
* `int64`
* `float32`
* `float64`
* `bool`
* `string`Encoding custom types is supported as well, with decoding custom types dependent on the type lookup function provided to the decoder's `SetDiscriminator` function.
## Testing
The discriminator functionality is thoroughly tested with:
* the tests from the `encoding/json` package
* ~300 encoding/decoding tests in [`discriminator_test.go`](./discriminator_test.go)
* canary testing of complex type models in the [`canaries`] directory, ex. the GoVmomi `VirtualMachineConfigInfo` structure ([`govmomi_test.go`](./canaries/govmomi_test.go))All of the above tests are executed:
* on every pull request
* push to the `main` branch
* via the GitHub action, [`test` workflow](./.github/workflows/test.yml)
* for all supported versions of Go, ex. 1.17.13, 1.18.9, 1.19.4, 1.20rc1It is also possible to run the tests locally with `make test`. This depends on either:
* an environment variable, `GO__BIN`, for each version of Go tested that points to the Go installation's `go` binary, ex. `GO_1.17.3_BIN="${HOME}/.go/1.17.3/bin/go"`
* or Docker, which is used to run the tests with the official Golang container imagesThe command `make test` will attempt to use a local Go installation for a given version of Golang, and if one cannot be found, default to using Docker. It is also possible to force the use of Docker with `DOCKER=1 make test`.
## License
This is a fork of Go's `encoding/json` package and so uses the same license as Golang.