2 回答

TA貢獻1785條經驗 獲得超4個贊
您可以做的是定義一個自定義解組函數并在那里決定是否要使用默認值。如果您有其他字段,StructTest您將需要為StructTestin創建一個別名,UnmarshalJSON以便其他字段仍將被視為相同,而V將被覆蓋。
下面的代碼片段顯示了一種方法,還可以查看這個有效的 Go 游樂場示例。
type StructTest struct {
V int64
Other string // this field should be unmarshaled the regular way
}
func (st *StructTest) UnmarshalJSON(data []byte) error {
// create alias to prevent endless loop
type Alias StructTest
tmp := struct {
*Alias
V *int64
}{
Alias: (*Alias)(st),
}
// unmarshal into temporary struct
err := json.Unmarshal(data, &tmp)
if err != nil {
return err
}
// check if V was supplied in JSON and set default value if it wasn't
if tmp.V == nil {
st.V = 1 // default
} else {
st.V = *tmp.V
}
return nil
}
編輯:
實際上對于這個簡單的例子,它可以做得更簡單:
func (st *StructTest) UnmarshalJSON(data []byte) error {
st.V = 1 // set default value before unmarshaling
type Alias StructTest // create alias to prevent endless loop
tmp := (*Alias)(st)
return json.Unmarshal(data, tmp)
}

TA貢獻1982條經驗 獲得超2個贊
簡短的回答是否定的,對于這樣的情況:
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,或者任何其他困難但現實的計算。這里的要點是,在某些情況下預加載默認值相對昂貴,因此我們希望避免這種情況。
- 2 回答
- 0 關注
- 429 瀏覽
添加回答
舉報