Polygo is a lightweight Go package for decoding polymorphic JSON responses effortlessly.
Dealing with APIs that return various types of objects can be challenging.
Polygo simplifies this process by allowing you to map your types into a common interface.
Consider an API endpoint /v1/shapes that returns a list of shapes, each defined by a type field:
[
{ "type": "circle", "radius": 5 },
{ "type": "square", "side": 3 }
]With Polygo, you can easily handle this polymorphic JSON response. Here's how.
- Create a Decoder: Initialize a decoder with a common interface and the field name used to check the object type.
- Register Types: Register your concrete types with the decoder.
- Unmarshal JSON: Use one of the available functions to unmarshal your JSON data.
// Define your shape interface
type Shape interface {
Area() float64
}
// Create a decoder specifying the interface and the field name,
// and register your concrete types
decoder := polygo.NewDecoder[Shape]("type").
Register("circle", Circle{}).
Register("square", Square{})
// unmarshal your JSON
shapes, _ := decoder.UnmarshalArray(jsonBytes)
for _, shape := range shapes {
// use the methods defined by the interface
fmt.Printf("shape area: %v\n", shape.Area())
// or check the concrete type if needed
switch s := shape.(type) {
case *Circle:
fmt.Printf("circle radius: %v\n", s.Radius)
case *Square:
fmt.Printf("square side: %v\n", s.Side)
}
}UnmarshalObject will unmarshal a plain object:
jsonBody := []byte(`{ "type": "circle", "radius": 5 }`)
shape, err := decoder.UnmarshalObject(jsonBody)
if err != nil {
return err
}UnmarshalArray will unmarshal an array of objects:
jsonBody := []byte(`[
{ "type": "circle", "radius": 5 },
{ "type": "square", "side": 3 }
]`)
shapes, err := decoder.UnmarshalArray(jsonBody)
if err != nil {
return err
}UnmarshalInnerObject will unmarshal an object, looking into the specified path (using the github.com/tidwall/gjson library).
jsonBody := []byte(`{
"data": { "type": "circle", "radius": 5 }
}`)
shapes, err := decoder.UnmarshalInnerObject("data", jsonBody)
if err != nil {
return err
}UnmarshalInnerArray will unmarshal an array of objects, looking into the specified path (using the github.com/tidwall/gjson library).
jsonBody := []byte(`{
"data": [
{ "type": "circle", "radius": 5 },
{ "type": "square", "side": 3 }
]
}`)
shapes, err := decoder.UnmarshalInnerArray("data", jsonBody)
if err != nil {
return err
}If your data is wrapped in an object with fields that you are interested to check, you should use a struct with a json.RawMessage field. Then you can unmarshal this field with the decoder.
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data json.RawMessage `json:"data"`
}
jsonData := []byte(`{
"code": 200,
"message": "all good",
"data": [
{ "type": "circle", "radius": 5 },
{ "type": "square", "side": 3 }
]
}`)
var resp Response
err := json.Unmarshal(jsonData, &resp)
if err != nil {
return err
}
shapes, err := decoder.UnmarshalArray(resp.Data)
if err != nil {
return err
}To use Polygo in your Go project, simply import it:
import "github.com/enrichman/polygo"Contributions are welcome! Feel free to open issues or pull requests on GitHub.
If you like the project please star it on Github 🌟, and feel free to drop me a note on Twitterhttps://twitter.com/enrichmann, or open an issue!