2016-12-05 124 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

非常感谢您花时间解释这一点。这非常有用。 –