临摹微笑
简短的回答是否定的,对于这样的情况:type T struct { Field1 int Field2 int}...func foo(data []byte) error { var x T if err := json.Unmarshal(data, &x); err != nil { return err } // now set Field1 and/or Field2 to default values if needed // ... but this is hard to do ... use(x) return nil}以另一种方式简单易行,即:func foo(data []byte) error { x := T{Field1: 99 /* red ballons */, Field2: 42 /* The Answer */ } if err := json.Unmarshal(data, &x); err != nil { return err } // now Field1 and/or Field2 are already set to default values if needed use(x) return nil}但是,如果默认值难以计算怎么办?例如:type T2 struct { Answer int Question string}和函数foo应该像以前一样有一个默认的答案 42,但是默认的问题应该是老鼠试图计算的那个,显然我们不想花几千年的时间来计算它,如果我们不需要的话。所以我们不能预先初始化x,我们需要知道是否提供了问题。1当然,另一种选择是解码为带有指针的结构,然后将该可为空的东西转换为没有指针的结构;我们知道可空变量的字段是否已填写,因为如果已填写,则它是非零的。这会产生这种代码:type T_ struct { Answer int Question *string}并填写一个x_类型的变量T_:func foo(data []byte) error { var x T2 x_ := T_{Answer: 42} if err := json.Unmarshal(data, &x_); err != nil { return err } x.Answer = x_.Answer if x_.Question = nil { x.Question = computeTheQuestion(x_.Answer) } else { x.Question = *x_.Question } use(x) return nil}然而,我再次感叹(至少是轻微地)使用像这个假设接口这样的代码解组 json 数据的能力:func foo(data []byte) error { var objectish map[string]interface{} if err := json.Unmarshal(data, &objectish); err != nil { return err } x := T2{Answer: 42} if err := json.ReUnmarshal(objectish, &x); err != nil { return err } // We now know that the object-ish decoded JSON has the right // "shape" for a T, and variable x is filled in with a default // Answer. Its Question is the empty string if there was an // empty Question, or if there was *no* Question at all, so // now let's find out if we need to compute the right Question. if _, ok := objectish["Question"]; !ok { x.Question = computeTheQuestion(x.Answer) } use(x) // pass x to a hoopy frood return nil}这个假设ReUnmarshal——它实际上可能只是它Unmarshal自己,真的——如果给定一个interface{},将把它的值视为从较早的结果得到的,Unmarshal并且只是重新输入结果。如果给定 a map[string]interface{},它将重新键入对象。如果给定 amap[string]map[string]interface{}它也会在这里做明显的事情,依此类推。用户看到的唯一更改是Unmarshal(或者ReUnmarshal,如果更改类型签名过于粗鲁)现在需要:[]byte, 或者string,可能,只是为了方便,或者json.RawMessage,因为这已经做了几乎正确的事情,或者map[string]T因为 T 是它接受的任何类型(包括map递归)它对每一个都做了明显的事情。请注意,这与使用json.RawMessage. 但是,当使用 时json.RawMessage,我们马上又要写出原始结构类型的变体,具有相同的字段名称。请参阅Go Playground 中的此示例,我们必须在其中声明x_(而不是objectish)使用json.RawMessage参数的类型。(或者,您可以创建一个具有自己的解组函数的类型,如lmazgon 的回答。但是您必须再次发明一种类型,而不仅仅是直接解码为已经提供的目标类型。)1在这种情况下,我们可以使用Question *string,或者假设不允许使用空字符串,或者其他什么,但这个例子是非常简化的。相反,假设我们应该计算一个具有精确 pi 到一些位置的 bignum,或者任何其他困难但现实的计算。这里的要点是,在某些情况下预加载默认值相对昂贵,因此我们希望避免这种情况。