2017-08-30 35 views
4

我正在將整數反序列化爲字符串struct字段。 struct字段是一個字符串,預計可以從我的庫的用戶分配。這就是爲什麼我希望它是一個字符串,因爲爲了將它寫入數據庫,我實際上並不關心裏面的值。 用戶可以提供文本,但有些只是分配整數。JSON將整數字段轉換爲字符串

考慮這樣的結構:

type Test struct { 
    Foo string 
} 

有時候,我結束了一個JSON值是有效的,但不會反序列化到結構由於富場是一個整數,而不是字符串:

{ "foo": "1" } // works 
{ "foo": 1 } // doesn't 

json.Unmarshal將炸燬,出現以下錯誤: json: cannot unmarshal number into Go struct field test.Foo of type string

見再現:https://play.golang.org/p/4Qau3umaVm

現在在其他所有JSON庫(使用其他語言)中,如果目標字段是字符串,並且您獲得一個整數,則反序列化器通常會將int整型包裝到一個字符串中,然後完成。這可以在Go中實現嗎?

因爲我實在無法控制數據是怎麼來的,我需要做json.Unmarshal不敏感這個 - 其他的解決辦法是定義爲富其中interface{}不必要我與類型的斷言等代碼複雜..

任何想法如何做到這一點?我基本上需要json:",string"的反例

+0

您可以實現的[json.Unmarshaler(https://golang.org/pkg/encoding/json/#Unmarshaler)接口。 – mkopriva

回答

5

要處理大的結構,你可以使用嵌入。

更新爲不丟棄可能以前設置的字段值。

func (t *T) UnmarshalJSON(d []byte) error { 
    type T2 T // create new type with same structure as T but without its method set! 
    x := struct{ 
     T2 // embed 
     Foo json.Number `json:"foo"` 
    }{T2: T2(*t)} // don't forget this, if you do and 't' already has some fields set you would lose them 

    if err := json.Unmarshal(d, &x); err != nil { 
     return err 
    } 
    *t = T(x.T2) 
    t.Foo = x.Foo.String() 
    return nil 
} 

https://play.golang.org/p/BytXCeHMvt

+3

很好的例子(但這不是[別名](https://golang.org/ref/spec#Alias_declarations)) – JimB

+0

我知道這不是1.9的別名,但wasn這種稱爲別名之前? – mkopriva

+3

不,它從來沒有被稱爲別名,因爲它創建了一個新類型。並不意味着別人過去沒有稱它爲「別名」,但是我多年來一直在盡力討論這個問題,特別是一旦提出了真正的別名想法;)唯一的_real_別名以前分別是'uint8'和'int32'的'byte'和'rune'。 – JimB

1

您可以通過實現json.Unamrshaler接口來自定義數據結構的拆分方式。

處理未知類型的最簡單的方法是將JSON nnmarshal成中間結構,和反序列化過程處理的類型的斷言和驗證:

type test struct { 
    Foo string `json:"foo"` 
} 

func (t *test) UnmarshalJSON(d []byte) error { 
    tmp := struct { 
     Foo interface{} `json:"foo"` 
    }{} 

    if err := json.Unmarshal(d, &tmp); err != nil { 
     return err 
    } 

    switch v := tmp.Foo.(type) { 
    case float64: 
     t.Foo = strconv.Itoa(int(v)) 
    case string: 
     t.Foo = v 
    default: 
     return fmt.Errorf("invalid value for Foo: %v", v) 
    } 

    return nil 
} 

+1

謝謝 - 我也想過。但是,這是一個非常大的結構..有沒有辦法,我可以如何才能實現unmarshaler只有有問題的領域? – Tigraine

+0

@Tigraine:是的mkopriva做了一個很好的例子 – JimB