我正在尝试从可能具有不同密钥的不同来源解组一些 JSON。例如,我可能有:
{
"a": 1,
"b": 2
}
Run Code Online (Sandbox Code Playgroud)
或者我可能有:
{
"c": 1,
"b": 2
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我可以保证“b”会在那里。但是,我希望“a”和“c”以相同的方式表示。实际上,我想要的是:
type MyJson struct {
Init int `json:"a",json:"c"`
Sec int `json:"b"
}
Run Code Online (Sandbox Code Playgroud)
基本上我希望解组器查找任一键并将其设置为Init
. 现在这实际上不起作用(或者我不会发布)。解组第一个给了我我想要的,而第二个设置Init
为 0。我理想的选择是解组到一个结构体,有两种可能性之一:
我尝试通过创建两个不同的结构和一个映射来实现数字 2,但似乎我无法创建一个类型为第二个值的映射:
type MyJson1 struct {
Init int `json:"a"`
Sec int `json:"b"`
}
type MyJson2 struct {
Init int `json:"c"`
Sec int `json:"b"`
}
Run Code Online (Sandbox Code Playgroud)
有没有办法定义一组行为类似于接口的结构?也就是说,它们都具有相同的字段,但定义不同。或者也许还有另一种方法。我可以使这些字段解组“a”和“c”其他字段,然后进行Init
相应的设置。但这不会超出几个变体。
谢谢!
icza 的方法很好,如果这个实现能够匹配 stdlib 的接口就更好了。
我建议实施json.Unmarshaler
:
// it can't get c itself.
type MyJson struct {
A int `json:"a"`
B int `json:"b"`
}
func (j *MyJson) UnmarshalJSON(b []byte) error {
type Alias MyJson
realValue := struct {
*Alias
C int `json:"c"` // <- now it can accept 'c' value
}{(*Alias)(j), 0}
if err := json.Unmarshal(b, &realValue); err != nil {
return err
} else if realValue.C != 0 {
// if C has value, overwrite A
realValue.A = realValue.C
}
return nil
}
Run Code Online (Sandbox Code Playgroud)
只需使用 对其进行解码即可json.Unmarshal
。
一种可能性是定义一个struct
包含可能输入的所有变体的字段,并为方便起见,为此结构提供一个方法,该方法将返回在输入中找到的字段:
type MyJson struct {
A *int `json:"a"`
C *int `json:"c"`
Sec int `json:"b"`
}
func (j *MyJson) Init() int {
if j.A == nil {
return *j.C
}
return *j.A
}
Run Code Online (Sandbox Code Playgroud)
使用它:
inputs := []string{
`{"a": 1, "b": 2}`,
`{"c": 1, "b": 2}`}
for _, input := range inputs {
var my MyJson
if err := json.Unmarshal([]byte(input), &my); err != nil {
panic(err)
}
fmt.Printf("Init: %v, Sec: %v\n", my.Init(), my.Sec)
}
Run Code Online (Sandbox Code Playgroud)
Init: 1, Sec: 2
Init: 1, Sec: 2
Run Code Online (Sandbox Code Playgroud)
在原始结构中,我们为 2 个可能的变体添加了 2 个字段。我将它们定义为指针,以便我们可以检测在 JSON 输入中找到了哪一个。现在,如果在解组之前我们将这些指针设置为指向相同的值,这就是我们所需要的:无论我们使用输入 JSON 的哪种变体,都会在内存中设置相同的值,因此您始终可以读取/引用结构体Init
字段:
type MyJson struct {
Init *int `json:"a"`
Init2 *int `json:"c"`
Sec int `json:"b"`
}
func main() {
inputs := []string{
`{"a": 1, "b": 2}`,
`{"c": 1, "b": 2}`}
for _, input := range inputs {
var my MyJson
my.Init = new(int) // Allocate an int
my.Init2 = my.Init // Set Init2 to point to the same value
if err := json.Unmarshal([]byte(input), &my); err != nil {
panic(err)
}
fmt.Printf("Init: %v, Sec: %v\n", *my.Init, my.Sec)
}
}
Run Code Online (Sandbox Code Playgroud)
在Go Playground上尝试一下。
您可以创建一个函数来创建并设置您的MyJson
解组准备,如下所示:
func NewMyJson() (my MyJson) {
my.Init = new(int) // Allocate an int
my.Init2 = my.Init // Set Init2 to point to the same value
return
}
Run Code Online (Sandbox Code Playgroud)
因此使用它变得如此简单:
var my = NewMyJson()
err := json.Unmarshal([]byte(input), &my)
Run Code Online (Sandbox Code Playgroud)
您可以指定该Init
字段不是指针,因为作为Init2
指针并指向就足够了Init
,因此这变得更加简单和理想(但NewMyJson
必须返回一个指针):
type MyJson struct {
Init int `json:"a"`
Init2 *int `json:"c"`
Sec int `json:"b"`
}
func NewMyJson() *MyJson {
my := new(MyJson)
my.Init2 = &my.Init // Set Init2 to point to Init
return my
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2585 次 |
最近记录: |