duanlao1552 2013-12-14 19:14
浏览 254
已采纳

Golang:嵌套JSON Unmarshaler遇到麻烦

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.

  • 写回答

2条回答 默认 最新

  • doudeng5218 2013-12-14 19:49
    关注

    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)
    ", 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:

    1. JSON library sees UnmarshalJSON on Wat because UnmarshalJSON from Somefin gets promoted to the outside
    2. JSON library cannot find any matching element in Somefin and delivers the whole input
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 孟德尔随机化结果不一致
  • ¥15 深度学习残差模块模型
  • ¥50 怎么判断同步时序逻辑电路和异步时序逻辑电路
  • ¥15 差动电流二次谐波的含量Matlab计算
  • ¥15 Can/caned 总线错误问题,错误显示控制器要发1,结果总线检测到0
  • ¥15 C#如何调用串口数据
  • ¥15 MATLAB与单片机串口通信
  • ¥15 L76k模块的GPS的使用
  • ¥15 请帮我看一看数电项目如何设计
  • ¥23 (标签-bug|关键词-密码错误加密)