英文:
Golang: Having trouble with nested JSON Unmarshaler
问题
给定以下代码:
package main
import (
"encoding/json"
"log"
)
type Somefin string
func (s *Somefin) UnmarshalJSON(b []byte) error {
log.Println("Unmarshaling", string(b))
*s = Somefin("~" + string(b) + "~")
return nil
}
type Wat struct {
A, B string
*Somefin
}
func main() {
b := []byte(`{"A":"foo","B":"bar","Somefin":"baz"}`)
w := &Wat{Somefin: new(Somefin)}
err := json.Unmarshal(b, w)
log.Println(w, err)
}
我得到了以下输出:
# go run wat.go
2013/12/14 13:59:17 Unmarshaling {"A":"foo","B":"bar","Somefin":"baz"}
2013/12/14 13:59:17 &{ <nil>} <nil>
所以 Somefin
键似乎尝试对整个结构进行解组,而不仅仅是它应该解组的键。我是做错了还是这是 json 编码器的一个 bug?顺便说一下,这是在 go 1.2 上的情况。
英文:
Given the following code:
package main
import (
"encoding/json"
"log"
)
type Somefin string
func (s *Somefin) UnmarshalJSON(b []byte) error {
log.Println("Unmarshaling",string(b))
*s = Somefin("~"+string(b)+"~")
return nil
}
type Wat struct {
A, B string
*Somefin
}
func main() {
b := []byte(`{"A":"foo","B":"bar","Somefin":"baz"}`)
w := &Wat{Somefin: new(Somefin)}
err := json.Unmarshal(b,w)
log.Println(w, err)
}
I get the following output:
# go run wat.go
2013/12/14 13:59:17 Unmarshaling {"A":"foo","B":"bar","Somefin":"baz"}
2013/12/14 13:59:17 &{ <nil>} <nil>
So the Somefin
key is for some reason trying to Unmarshal the entire structure instead of just the key it ought to. Am I doing this wrong or is this a bug in the json encoder? This is on go 1.2, btw.
答案1
得分: 4
为什么最后没有结果?
这不是解码器的错误,而是你的代码中的错误。你只是在UnmarshalJSON
中给本地指针s
分配了另一个地址。修正后的代码如下:
func (s *Somefin) UnmarshalJSON(b []byte) error {
log.Println("Unmarshaling", string(b))
sn := Somefin("~" + string(b) + "~")
*s = sn
return nil
}
s = &sn
的语义是将地址&sn
赋给s
。这类似于s = 42
。
*s = sn
的语义是将sn
的内容复制到s
指向的位置。
这要求s
指向一个有效的内存位置,不能为nil
。你的代码示例用法如下(play):
w := &Wat{Somefin: new(Somefin)}
err := json.Unmarshal(b, w)
log.Printf("%#v (%s)\n", w, err)
关键是使用new(Somefin)
对Wat
进行初始化,以使UnmarshalJSON
中的指针s
有效(指向使用new(Somefin)
创建的对象)。
为什么在UnmarshalJSON
中获取整个字符串?
嵌入不是多态。虽然嵌入对象的方法集(在你的例子中是Somefin
)被提升到外部,但这并不意味着该方法现在在嵌入结构上工作,而不是在嵌入的结构上工作。
小例子(play):
type Inner struct { a int }
func (i *Inner) A() int { return i.a }
type Outer struct {
*Inner
a int
}
i := &Inner{}
o := Outer{Inner: i}
fmt.Println("Inner.A():", i.A())
fmt.Println("Outer.A():", o.A())
o.a = 2
fmt.Println("Outer.A():", o.A())
使用多态,你期望Outer.A()
返回2
,因为方法A()
将在Outer
的范围内而不是Inner
的范围内运行。使用嵌入,作用域永远不会改变,A()
将始终在Inner
上运行。
相同的效果使得你的UnmarshalJSON
无法看到Somefin
中的两个成员A
和B
,因为它们根本不在Somefin
的范围内:
- JSON库在
Wat
上看到UnmarshalJSON
,因为Somefin
的UnmarshalJSON
被提升到外部。 - JSON库在
Somefin
中找不到任何匹配的元素,并返回整个输入。
英文:
Why you are getting no result at the end
This is no bug in the decoder, it is a bug in your code. You're just assigning another address
to the local pointer s
in UnmarshalJSON
. Corrected code:
func (s *Somefin) UnmarshalJSON(b []byte) error {
log.Println("Unmarshaling",string(b))
sn := Somefin("~"+string(b)+"~")
*s = sn
return nil
}
Semantics of s = &sn
: Assign the address &sn
to s
. This is similar to s = 42
.
Semantics of *s = sn
: Copy whatever is sn
to the place where s
points to.
One requirement for this to work is that s
points to a valid memory location and must not be nil
.
Example usage of your code (play):
w := &Wat{Somefin: new(Somefin)}
err := json.Unmarshal(b,w)
log.Printf("%#v (%s)\n", w, err)
Crucial is the initialization of Wat
with a new Somefin
so that the pointer s
in
UnmarshalJSON
is valid (points to the object created with new(Somefin)
).
Why you are getting the whole string in UnmarshalJSON
Embedding is not polymorphism. While the method set of the embedded object (in your case
Somefin
) is promoted to the outside, this does not mean that the method is now working
on the embedding struct rather than the embedded one.
Small example (play):
type Inner struct { a int }
func (i *Inner) A() int { return i.a }
type Outer struct {
*Inner
a int
}
i := &Inner{}
o := Outer{Inner: i}
fmt.Println("Inner.A():", i.A())
fmt.Println("Outer.A():", o.A())
o.a = 2
fmt.Println("Outer.A():", o.A())
With polymorphism you would expect Outer.A()
to return 2
as method A()
would operate in
the scope of Outer
and not Inner
anymore. With embedding the scope is never changed and
A()
will always remain operating on Inner
.
The same effect prevents your UnmarshalJSON
from seeing the two members A
and B
as these
are simply not in the scope of Somefin
:
- JSON library sees
UnmarshalJSON
onWat
becauseUnmarshalJSON
fromSomefin
gets promoted to the outside - JSON library cannot find any matching element in
Somefin
and delivers the whole input
答案2
得分: 2
我弄清楚了。
如果你的结构定义如下:
type Wat struct {
A, B string
Somefin
}
那么我在原始问题中描述的错误就会发生。但是如果你这样做:
type Wat struct {
A, B string
Somefin Somefin
}
那么就不会发生这个错误。请查看Chris对这个答案的评论,他对此进行了很好的解释。
英文:
I figured this out.
If you have the struct definition like this:
type Wat struct {
A, B string
Somefin
}
Then the error I described in the OP happens. But if you do:
type Wat struct {
A, B string
Somefin Somefin
}
Then it doesn't. Check out Chris's comment to this answer for a good explanation of why.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论