This posts presents a use case for json.RawMessage
from encoding/json
package.
Imagine you have an API for a fast food restaurant which process the following JSON:
[
{
"Type": "Cheeseburguer",
"Params": {
"ExtraCheese": true
}
},
{
"Type": "Coke",
"Params": {
"Diet": false,
"Size": "L"
}
},
{
"Type": "French Fries",
"Params": {
"Size": "S",
"Ketchup": true
}
}
]
A first attempt to model that, you will need the following structs to unmarshal that JSON:
type CheeseburguerParams struct {
ExtraCheese bool
}
type CokeParams struct {
Diet bool
Size string
}
type FrenchFriesParams struct {
Size string
Ketchup bool
}
type Item struct {
Type string
Params map[string]interface{}
}
type Order struct {
Items []Item
}
To process specifi Item
params you have 2 options:
- Read and build params for each type
- Marshal
map[string]interface{}
to[]byte
and based on type, unmarshal it to its specific param
The first one you have to create a function that reads a generic map[string]interface{}
and populate a param.
The second one is like JSON.stringify(JSON.parse())
from Javascript.
It is easier, because you don’t have to worry about type casting, populate, etc.
But it takes an extra json.Marshal
and json.Unmarshal
.
Let’s take a lazy approach without doing an extra json.Marshal
.
Here comes json.RawMessage
.
If you model the Item
struct like this:
type Item struct {
Type string
Params json.RawMessage
}
The json.Unmarshal
puts the []byte
inside the Params
field.
Than you just need to switch on Type
field and do the last json.Unmarshal
:
for _, item := range order.Items {
switch item.Type {
case "Cheeseburger":
var param CheeseburgerParam
if err := json.Unmarshal(item.Params, ¶m); err != nil {
log.Error(err)
}
processCheeseburguer(param)
case "French Fries":
var param FrenchFriesParam
if err := json.Unmarshal(item.Params, ¶m); err != nil {
log.Error(err)
}
processFrenchFries(param)
case "Coke":
var param CokeParam
if err := json.Unmarshal(item.Params, ¶m); err != nil {
log.Error(err)
}
processCoke(param)
}
}
That is easier to process dynamic messages with parameters from a list, or message broker, or anything else, by postponing
the processing of the extra information using json.RawMessage
.
The interesting part, is that it don’t work if you use []byte
type for Param
field.
Because json.Unmarhsal
tries to reads an array which is not there in the JSON.
Check this: https://play.golang.org/p/xUIsUB8GAi7