可以部分解码和更新 JSON 吗?(go)

huangapple go评论78阅读模式
英文:

Is it possible to partially decode and update JSON? (go)

问题

我需要解码和更新 JSON 对象中的特定值。问题是我不知道对象的完整结构。encoding/json 包在编码时会“忽略”/截断未在结构体中提供的字段,因此在编码时这些字段会丢失。我想知道是否可能只解组我知道的结构,然后更新它,并在编码时不截断/删除未知的结构/信息。

英文:

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.

答案1

得分: 3

我知道这是一个相当旧的问题,但是我学到了通常的结构和json.RawMessage的组合可以在这种情况下完成工作。让我分享一下。

关键是:将整个数据保存在raw字段中,并将其用于编码/解码。其他字段可以从中派生出来。

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))
}

输出应该像这样:

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"}

注意:这不保留键的顺序或缩进。

英文:

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.

答案2

得分: 1

似乎是可能的。

package main

import (
	"encoding/json"
	"fmt"
	"log"
)

func main() {
	type Color struct {
		Space string
		Point json.RawMessage // 延迟解析,直到我们知道颜色空间
	}
	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("错误:", err)
	}
	colors.Space = "无空间"

	b, err := json.Marshal(&colors)
	if err != nil {
		panic(err)
	}
	fmt.Printf("b 现在是 %s", b)
	return
}
英文:

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
}

huangapple
  • 本文由 发表于 2015年3月18日 07:47:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/29111777.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定