Reputation: 10846
I need to decode and update only a specific value of a json object. The issue is that I don't know the full structure of the object. The encoding/json package "ignores"/truncates the fields not provided in the struct so on encoding these fields are lost. I'm wondering if it's possible to unmarshal only the structure I know, update it and then marshal it without to truncate/remove the unknown structure/information.
Upvotes: 3
Views: 4824
Reputation: 150
I know this is quite old question, but I learned combination of usual struct and json.RawMessage
will do the job in the situation. Let me share.
The point is: hold entire data into raw
field, and use that for encoding/decoding. Other fields can be derived from there.
package main
import (
"encoding/json"
"log"
)
type Color struct {
Space string
raw map[string]json.RawMessage
}
func (c *Color) UnmarshalJSON(bytes []byte) error {
if err := json.Unmarshal(bytes, &c.raw); err != nil {
return err
}
if space, ok := c.raw["Space"]; ok {
if err := json.Unmarshal(space, &c.Space); err != nil {
return err
}
}
return nil
}
func (c *Color) MarshalJSON() ([]byte, error) {
bytes, err := json.Marshal(c.Space)
if err != nil {
return nil, err
}
c.raw["Space"] = json.RawMessage(bytes)
return json.Marshal(c.raw)
}
func main() {
before := []byte(`{"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}}`)
log.Println("before: ", string(before))
// decode
color := new(Color)
err := json.Unmarshal(before, color)
if err != nil {
log.Fatal(err)
}
// modify fields of interest
color.Space = "RGB"
// encode
after, err := json.Marshal(color)
if err != nil {
log.Fatal(err)
}
log.Println("after: ", string(after))
}
The output should be like this:
2020/09/03 01:11:33 before: {"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}}
2020/09/03 01:11:33 after: {"Point":{"Y":255,"Cb":0,"Cr":-10},"Space":"RGB"}
NB: this doesn't preserve key order or indentations.
Upvotes: 3
Reputation: 10846
It seems it's possible.
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
type Color struct {
Space string
Point json.RawMessage // delay parsing until we know the color space
}
type RGB struct {
R uint8
G uint8
B uint8
}
type YCbCr struct {
Y uint8
Cb int8
Cr int8
}
var j = []byte(`
{"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}}`)
var colors Color
err := json.Unmarshal(j, &colors)
if err != nil {
log.Fatalln("error:", err)
}
colors.Space = "no-space"
b, err := json.Marshal(&colors)
if err != nil {
panic(err)
}
fmt.Printf("b is now %s", b)
return
}
Upvotes: 1