使用“有效”的零值解析JSON

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

Unmarshal JSON with "valid" zero values

问题

我有一些JSON数据,我正在将其解组成不同的结构体,以便我可以处理数据,似乎这是项目中最困难的部分!

这个JSON的格式是,如果字段缺失,则它实际上是nil。这是继承自https://stackoverflow.com/questions/40970422/default-struct-values的问题,但我认为它值得在SO上提出自己的问题。

因此,零是一个有效的值,我需要能够在我的Go代码中区分出这一点。是否有一种方法可以让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("应该打印int的值,而不是指针... %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("RawMessage中的字段数量错误:%d != %d", g, e)
	}
	return nil
}
英文:

I have some JSON that I am unmarshalling into various structs so that I can then process the data, seemingly this is turning into the hardest part of the project!!!

The format of this JSON is that if the field is missing then it is essentially nil. This is following on from https://stackoverflow.com/questions/40970422/default-struct-values but thought it deserved it's own question on SO.

A zero therefore is a valid value and I need to be able to discern this in my Go code. Is there a way to get Go to unmarshal into this struct with pointers at all?

In the example playground you can see what I mean, it "appears" to work but when I come to print out one of the pointer values it always prints the pointer address and not the actual value.

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
}

答案1

得分: 4

你的代码是正确的。如果你想要获取值,可以将指针与nil进行比较,并解引用指针:

fmt.Printf("应该打印int的值,而不是指针... %d", *o.ClientData.MissingInt)
英文:

Your code is correct. Test the pointer against nil and dereference the pointer if you want the value:

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

答案2

得分: 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("应该打印int的值,而不是指针... %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}
应该打印int的值,而不是指针... 0

从上面的输出可以看出,MissingXX字段显示为静态值<missing>

**注意:**如果你使用%v动词,你需要实现fmt.Stringer接口,它与GoStringer接口基本相同,只是方法名不是GoString()而是String()

英文:

Your confusion arises from the default formatting of the fmt package, which prints the hex value in case of pointer fields, and not the pointed value.

If you're using the %#v verb for printing, you may implement the fmt.GoStringer interface on your struct to override this "behavior":

func (c ClientData) GoString() string {
mi := &quot;&lt;missing&gt;&quot;
if c.MissingInt != nil {
mi = strconv.Itoa(*c.MissingInt)
}
ms := &quot;&lt;missing&gt;&quot;
if c.MissingString != nil {
ms = *c.MissingString
}
return fmt.Sprintf(&quot;{TestCode: %s, MissingInt: %s, DefaultInt: %d, MissingString: %s, DefaultString: %s}&quot;,
c.TestCode, mi, c.DefaultInt, ms, c.DefaultString)
}

And changing the last printing line to:

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

Your output becomes more readable (try it on the Go Playground):

main.RawMessage{AlwaysString:&quot;AAAAAA&quot;, ClientData:{TestCode: Sss, MissingInt: &lt;missing&gt;, DefaultInt: 0, MissingString: &lt;missing&gt;, DefaultString: }, ReceptionTime:123456}
main.RawMessage{AlwaysString:&quot;AAAAAA&quot;, ClientData:{TestCode: Sss, MissingInt: 0, DefaultInt: 0, MissingString: , DefaultString: }, ReceptionTime:123456}
Should print the value of the int, not pointer... 0

As you can see from the output above, the MissingXX fields have the static value &lt;missing&gt; displayed.

Note: If you were to use the %v verb, you'd have to implement the fmt.Stringer interface, which is basically the same, just the method name is not GoString() but simply String().

huangapple
  • 本文由 发表于 2016年12月5日 17:57:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/40971542.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定