2016-12-05 118 views
1

我有一些JSON可以解構到各種結構中,這樣我就可以處理數據,看起來這正在變成項目中最難的部分!具有「有效」零值的解組JSON

這個JSON的格式是,如果該字段丟失,那麼它基本上是零。這是從Default struct values以下,但認爲它值得它自己的問題上SO。

零因此是一個有效的值,我需要能夠在我的Go代碼中辨別出這個值。有沒有辦法使用指針去解組到這個結構?

在示例playground中,您可以看到我的意思,它「出現」可以工作,但是當我打印出其中一個指針值時,它總是打印指針地址而不是實際值。

package main 

import "fmt" 
import "log" 
import "encoding/json" 

const inputMissing = ` 
["AAAAAA", {"testCode" : "Sss"}, 123456] 
` 

const inputZero = ` 
["AAAAAA", {"testCode" : "Sss", "missingInt" : 0, "defaultInt" : 0,"missingString" : "", "defaultString" : ""}, 123456] 
` 

type RawMessage struct { 
    AlwaysString string 
    ClientData ClientData 
    ReceptionTime int 
} 

type ClientData struct { 
    TestCode string 
    MissingInt *int 
    DefaultInt int 

    MissingString *string 
    DefaultString string 
} 

func main() { 

    var n RawMessage 
    if err := json.Unmarshal([]byte(inputMissing), &n); err != nil { 
     log.Fatal(err) 
    } 

    fmt.Printf("%#v\n", n) 

    var o RawMessage 
    if err := json.Unmarshal([]byte(inputZero), &o); err != nil { 
     log.Fatal(err) 
    } 

    fmt.Printf("%#v\n", o) 

    fmt.Printf("Should print the value of the int, not pointer... %i", o.ClientData.MissingInt) 
} 

func (n *RawMessage) UnmarshalJSON(buf []byte) error { 
    tmp := []interface{}{&n.AlwaysString, &n.ClientData, &n.ReceptionTime} 
    wantLen := len(tmp) 
    if err := json.Unmarshal(buf, &tmp); err != nil { 
     return err 
    } 
    if g, e := len(tmp), wantLen; g != e { 
     return fmt.Errorf("wrong number of fields in RawMessage: %d != %d", g, e) 
    } 
    return nil 
} 
+0

我不明白。如果你需要這個值,你爲什麼不直接檢查'nil'的指針然後解除引用呢? –

+0

我不確定unmarshal是否正確設置值。無論我做什麼,我都無法從'MissingInt'或'MissingString'讀取0(或任何其他值) –

+0

也許一個更好的例子是https://play.golang.org/p/RWKCFtlhMk,我不能打印/閱讀555或測試 –

回答

2

您的代碼是正確的。測試對零和間接引用指針的指針,如果你想要的值:

fmt.Printf("Should print the value of the int, not pointer... %d", *o.ClientData.MissingInt) 
+0

對不起,我錯過了!我現在覺得自己像個白癡! –

+1

你不應該。沒有這樣的事情是一個愚蠢的問題。 –

+0

謝謝!當使用指針而不是結構體中的值時,我是否必須小心內存泄漏,或者當我完成對象時,GC仍會踢入整個結構體? –

1

你的困惑源於fmt包的默認格式,打印在指針字段的情況下,十六進制值,而不是尖銳的價值。

如果您正在使用%#v動詞進行打印,你可以實現你的結構來覆蓋這個「行爲」的fmt.GoStringer接口:

func (c ClientData) GoString() string { 
    mi := "<missing>" 
    if c.MissingInt != nil { 
     mi = strconv.Itoa(*c.MissingInt) 
    } 
    ms := "<missing>" 
    if c.MissingString != nil { 
     ms = *c.MissingString 
    } 

    return fmt.Sprintf("{TestCode: %s, MissingInt: %s, DefaultInt: %d, MissingString: %s, DefaultString: %s}", 
     c.TestCode, mi, c.DefaultInt, ms, c.DefaultString) 
} 

和不斷變化的最後的印刷線:

fmt.Printf("Should print the value of the int, not pointer... %d", 
    *o.ClientData.MissingInt) 

你的輸出變得更具有可讀性(嘗試在Go Playground):

main.RawMessage{AlwaysString:"AAAAAA", ClientData:{TestCode: Sss, MissingInt: <missing>, DefaultInt: 0, MissingString: <missing>, DefaultString: }, ReceptionTime:123456} 
main.RawMessage{AlwaysString:"AAAAAA", ClientData:{TestCode: Sss, MissingInt: 0, DefaultInt: 0, MissingString: , DefaultString: }, ReceptionTime:123456} 
Should print the value of the int, not pointer... 0 

從上面的輸出中可以看到,MissingXX字段的靜態值顯示爲<missing>

注:如果你使用%v動詞,你必須實現fmt.Stringer接口,這基本上是一樣的,只是方法名是不GoString()而只是String()

+0

非常感謝您花時間解釋這一點。這非常有用。 –