亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

將json解組為結構時如何設置默認值

將json解組為結構時如何設置默認值

Go
30秒到達戰場 2022-04-20 20:50:32
從 json 字符串解組時,我想為字段設置默認值。我知道我可以在解組之前設置我想要的值,我認為這不是一個漂亮的方式。還有其他方法嗎,例如使用“默認”標簽?func main() {    in := "{}"    myStruct := StructTest{}    json.Unmarshal([]byte(in), &myStruct)    fmt.Println(myStruct)}type StructTest struct {    V int64 `default:1`}
查看完整描述

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)

}


查看完整回答
反對 回復 2022-04-20
?
臨摹微笑

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,或者任何其他困難但現實的計算。這里的要點是,在某些情況下預加載默認值相對昂貴,因此我們希望避免這種情況。


查看完整回答
反對 回復 2022-04-20
  • 2 回答
  • 0 關注
  • 429 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號