json.Unmarshal在嵌入类型具有UnmarshalJSON时失败。

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

json.Unmarshal fails when embedded type has UnmarshalJSON

问题

我正在尝试解组一个包含嵌入类型的结构体。当嵌入类型具有UnmarshalJSON方法时,外部类型的解组失败:

链接:https://play.golang.org/p/Y_Tt5O8A1Q

package main


import (
	"fmt"

	"encoding/json"
)

type Foo struct {
	EmbeddedStruct
	Field string
}

func (d *Foo) UnmarshalJSON(from []byte) error {
	fmt.Printf("Foo.UnmarshalJSON\n")

	type Alias Foo
	alias := &Alias{}
	if err := json.Unmarshal(from, alias); err != nil {
		return fmt.Errorf("Error in Foo.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n", err)
	}
	*d = Foo(*alias)

	return nil
}

type EmbeddedStruct struct {
	EmbeddedField string
}

func (d *EmbeddedStruct) UnmarshalJSON(from []byte) error {
	fmt.Printf("EmbeddedStruct.UnmarshalJSON\n")

	type Alias EmbeddedStruct
	alias := &Alias{}
	if err := json.Unmarshal(from, alias); err != nil {
		return fmt.Errorf("Error in EmbeddedStruct.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n", err)
	}
	*d = EmbeddedStruct(*alias)

	return nil
}

func main() {

	data := `{"EmbeddedField":"embeddedValue", "Field": "value"}`
	foo := &Foo{}

	json.Unmarshal([]byte(data), foo)

	fmt.Printf("Foo: %v\n", foo)

	if foo.EmbeddedField != "embeddedValue" {
		fmt.Printf("Unmarshal didn't work, EmbeddedField value is %v. Should be 'embeddedValue'\n", foo.EmbeddedField)
	}

	if foo.Field != "value" {
		fmt.Printf("Unmarshal didn't work, Field value is %v. Should be 'value'\n", foo.Field)
	}

}

输出结果为:

Foo.UnmarshalJSON
EmbeddedStruct.UnmarshalJSON
Foo: &{{embeddedValue} }
Unmarshal didn't work, Field value is . Should be 'value'
...所以两个自定义解组函数都运行了。嵌入结构体的值是正确的,但外部结构体的值丢失了。
如果我们简单地删除EmbeddedStruct.UnmarshalJSON方法,它将按预期工作。
我做错了什么吗?这是预期的行为吗?还是一个错误?我确定有一种方法可以调整我的UnmarshalJSON方法使其正常工作。
<details>
<summary>英文:</summary>
I&#39;m trying to unmarshal a struct that has an embedded type. When the embedded type has an UnmarshalJSON method, the unmarshaling of the outer type fails:
https://play.golang.org/p/Y_Tt5O8A1Q
package main
import (
&quot;fmt&quot;
&quot;encoding/json&quot;
)
type Foo struct {
EmbeddedStruct
Field string
}
func (d *Foo) UnmarshalJSON(from []byte) error {
fmt.Printf(&quot;Foo.UnmarshalJSON\n&quot;)
type Alias Foo
alias := &amp;Alias{}
if err := json.Unmarshal(from, alias); err != nil {
return fmt.Errorf(&quot;Error in Foo.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n&quot;, err)
}
*d = Foo(*alias)
return nil
}
type EmbeddedStruct struct {
EmbeddedField string
}
func (d *EmbeddedStruct) UnmarshalJSON(from []byte) error {
fmt.Printf(&quot;EmbeddedStruct.UnmarshalJSON\n&quot;)
type Alias EmbeddedStruct
alias := &amp;Alias{}
if err := json.Unmarshal(from, alias); err != nil {
return fmt.Errorf(&quot;Error in EmbeddedStruct.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n&quot;, err)
}
*d = EmbeddedStruct(*alias)
return nil
}
func main() {
data := `{&quot;EmbeddedField&quot;:&quot;embeddedValue&quot;, &quot;Field&quot;: &quot;value&quot;}`
foo := &amp;Foo{}
json.Unmarshal([]byte(data), foo)
fmt.Printf(&quot;Foo: %v\n&quot;, foo)
if foo.EmbeddedField != &quot;embeddedValue&quot; {
fmt.Printf(&quot;Unmarshal didn&#39;t work, EmbeddedField value is %v. Should be &#39;embeddedValue&#39;\n&quot;, foo.EmbeddedField)
}
if foo.Field != &quot;value&quot; {
fmt.Printf(&quot;Unmarshal didn&#39;t work, Field value is %v. Should be &#39;value&#39;\n&quot;, foo.Field)
}
}
The output is:
Foo.UnmarshalJSON
EmbeddedStruct.UnmarshalJSON
Foo: &amp;{{embeddedValue} }
Unmarshal didn&#39;t work, Field value is . Should be &#39;value&#39;
... so both custom unmarshal functions ran. The value from the embedded struct is correct, but the value from the outer struct is lost.
If we simply remove the EmbeddedStruct.UnmarshalJSON method, it works as expected.
Am I doing something wrong? Is this expected? Or a bug? I&#39;m sure there&#39;s a way I can tweak my UnmarshalJSON methods to get it working.
</details>
# 答案1
**得分**: 10
预计的结果。
当你创建别名时:
type Alias Foo
`Alias` 不会继承 `Foo` 的方法,因为它是一个不同的类型,具有不同的方法集,这正是你想要实现的,以避免无限递归。
然而,嵌入的 `EmbeddedStruct` 的 `UnmarshalJSON` 方法将被提升!
因此,`Alias` 将拥有一个 `UnmarshalJSON` 方法,它只会解析 `EmbeddedStruct` 的值,而不是使用你期望的默认解析方式。
<details>
<summary>英文:</summary>
It is expected.
When you create the alias:
type Alias Foo
`Alias` will not inherit the methods of `Foo` since it is a different type with a different method set, which is what you wanted to achieve to avoid an infinite recursion.
_However_, the embedded `EmbeddedStruct`&#39;s `UnmarshalJSON` method will instead be promoted!
So, `Alias` will have an `UnmarshalJSON` method that will only unmarshal `EmbeddedStruct`&#39;s value, instead of using the default unmarshalling that you desired.
</details>

huangapple
  • 本文由 发表于 2015年4月16日 14:52:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/29667379.html
匿名

发表评论

匿名网友

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

确定