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

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

json.Unmarshal fails when embedded type has UnmarshalJSON

问题

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

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

  1. package main
  2. import (
  3. "fmt"
  4. "encoding/json"
  5. )
  6. type Foo struct {
  7. EmbeddedStruct
  8. Field string
  9. }
  10. func (d *Foo) UnmarshalJSON(from []byte) error {
  11. fmt.Printf("Foo.UnmarshalJSON\n")
  12. type Alias Foo
  13. alias := &Alias{}
  14. if err := json.Unmarshal(from, alias); err != nil {
  15. return fmt.Errorf("Error in Foo.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n", err)
  16. }
  17. *d = Foo(*alias)
  18. return nil
  19. }
  20. type EmbeddedStruct struct {
  21. EmbeddedField string
  22. }
  23. func (d *EmbeddedStruct) UnmarshalJSON(from []byte) error {
  24. fmt.Printf("EmbeddedStruct.UnmarshalJSON\n")
  25. type Alias EmbeddedStruct
  26. alias := &Alias{}
  27. if err := json.Unmarshal(from, alias); err != nil {
  28. return fmt.Errorf("Error in EmbeddedStruct.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n", err)
  29. }
  30. *d = EmbeddedStruct(*alias)
  31. return nil
  32. }
  33. func main() {
  34. data := `{"EmbeddedField":"embeddedValue", "Field": "value"}`
  35. foo := &Foo{}
  36. json.Unmarshal([]byte(data), foo)
  37. fmt.Printf("Foo: %v\n", foo)
  38. if foo.EmbeddedField != "embeddedValue" {
  39. fmt.Printf("Unmarshal didn't work, EmbeddedField value is %v. Should be 'embeddedValue'\n", foo.EmbeddedField)
  40. }
  41. if foo.Field != "value" {
  42. fmt.Printf("Unmarshal didn't work, Field value is %v. Should be 'value'\n", foo.Field)
  43. }
  44. }

输出结果为:

  1. Foo.UnmarshalJSON
  2. EmbeddedStruct.UnmarshalJSON
  3. Foo: &{{embeddedValue} }
  4. Unmarshal didn't work, Field value is . Should be 'value'
  5. ...所以两个自定义解组函数都运行了。嵌入结构体的值是正确的,但外部结构体的值丢失了。
  6. 如果我们简单地删除EmbeddedStruct.UnmarshalJSON方法,它将按预期工作。
  7. 我做错了什么吗?这是预期的行为吗?还是一个错误?我确定有一种方法可以调整我的UnmarshalJSON方法使其正常工作。
  8. <details>
  9. <summary>英文:</summary>
  10. 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:
  11. https://play.golang.org/p/Y_Tt5O8A1Q
  12. package main
  13. import (
  14. &quot;fmt&quot;
  15. &quot;encoding/json&quot;
  16. )
  17. type Foo struct {
  18. EmbeddedStruct
  19. Field string
  20. }
  21. func (d *Foo) UnmarshalJSON(from []byte) error {
  22. fmt.Printf(&quot;Foo.UnmarshalJSON\n&quot;)
  23. type Alias Foo
  24. alias := &amp;Alias{}
  25. if err := json.Unmarshal(from, alias); err != nil {
  26. return fmt.Errorf(&quot;Error in Foo.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n&quot;, err)
  27. }
  28. *d = Foo(*alias)
  29. return nil
  30. }
  31. type EmbeddedStruct struct {
  32. EmbeddedField string
  33. }
  34. func (d *EmbeddedStruct) UnmarshalJSON(from []byte) error {
  35. fmt.Printf(&quot;EmbeddedStruct.UnmarshalJSON\n&quot;)
  36. type Alias EmbeddedStruct
  37. alias := &amp;Alias{}
  38. if err := json.Unmarshal(from, alias); err != nil {
  39. return fmt.Errorf(&quot;Error in EmbeddedStruct.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n&quot;, err)
  40. }
  41. *d = EmbeddedStruct(*alias)
  42. return nil
  43. }
  44. func main() {
  45. data := `{&quot;EmbeddedField&quot;:&quot;embeddedValue&quot;, &quot;Field&quot;: &quot;value&quot;}`
  46. foo := &amp;Foo{}
  47. json.Unmarshal([]byte(data), foo)
  48. fmt.Printf(&quot;Foo: %v\n&quot;, foo)
  49. if foo.EmbeddedField != &quot;embeddedValue&quot; {
  50. fmt.Printf(&quot;Unmarshal didn&#39;t work, EmbeddedField value is %v. Should be &#39;embeddedValue&#39;\n&quot;, foo.EmbeddedField)
  51. }
  52. if foo.Field != &quot;value&quot; {
  53. fmt.Printf(&quot;Unmarshal didn&#39;t work, Field value is %v. Should be &#39;value&#39;\n&quot;, foo.Field)
  54. }
  55. }
  56. The output is:
  57. Foo.UnmarshalJSON
  58. EmbeddedStruct.UnmarshalJSON
  59. Foo: &amp;{{embeddedValue} }
  60. Unmarshal didn&#39;t work, Field value is . Should be &#39;value&#39;
  61. ... so both custom unmarshal functions ran. The value from the embedded struct is correct, but the value from the outer struct is lost.
  62. If we simply remove the EmbeddedStruct.UnmarshalJSON method, it works as expected.
  63. 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.
  64. </details>
  65. # 答案1
  66. **得分**: 10
  67. 预计的结果。
  68. 当你创建别名时:
  69. type Alias Foo
  70. `Alias` 不会继承 `Foo` 的方法,因为它是一个不同的类型,具有不同的方法集,这正是你想要实现的,以避免无限递归。
  71. 然而,嵌入的 `EmbeddedStruct` 的 `UnmarshalJSON` 方法将被提升!
  72. 因此,`Alias` 将拥有一个 `UnmarshalJSON` 方法,它只会解析 `EmbeddedStruct` 的值,而不是使用你期望的默认解析方式。
  73. <details>
  74. <summary>英文:</summary>
  75. It is expected.
  76. When you create the alias:
  77. type Alias Foo
  78. `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.
  79. _However_, the embedded `EmbeddedStruct`&#39;s `UnmarshalJSON` method will instead be promoted!
  80. So, `Alias` will have an `UnmarshalJSON` method that will only unmarshal `EmbeddedStruct`&#39;s value, instead of using the default unmarshalling that you desired.
  81. </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:

确定