JSON unmarshal integer field into a string

对着背影说爱祢 提交于 2020-08-21 06:46:34

问题


I am struggling with deserializing a integer into a string struct field. The struct field is a string and is expected to be assignable from users of my library. That's why I want it to be a string, since for the purpose of writing it to the database I actually don't care about the value inside. The users can supply text, but some just assign integers.

Consider this struct:

type Test struct {
  Foo string
}

Sometimes I end up with a JSON value that is valid but won't deserialize into the struct due to the Foo field being a integer instead of a string:

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

json.Unmarshal will blow up with the following error: json: cannot unmarshal number into Go struct field test.Foo of type string

See the reproduction: https://play.golang.org/p/4Qau3umaVm

Now in every other JSON library (in other languages) I have worked in so far, if the target field is a string and you get a integer the deserializer will usually just wrap the int in a string and be done with it. Can this be achieved in Go?

Since I can't really control how the data comes in I need to make json.Unmarshal unsensitive to this - the other solution would be to define Foo as interface{} which needlessly complicates my code with type assertions etc..

Any ideas on how to do this? I basically need the inverse of json:",string"


回答1:


To handle big structs you can use embedding.

Updated to not discard possibly previously set field values.

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




回答2:


You can customize how the data structure is unamrshaled by implementing the json.Unamrshaler interface.

The simplest way to handle unknown types is to nnmarshal the JSON into an intermediate structure, and handle the type assertions and validation during deserialization:

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
}

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



来源:https://stackoverflow.com/questions/45968476/json-unmarshal-integer-field-into-a-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!