doumei3828
doumei3828
2017-08-30 19:37

JSON解组整数字段为字符串

已采纳

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"

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • dotif64826 dotif64826 4年前

    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

    点赞 评论 复制链接分享
  • dpoxk64080 dpoxk64080 4年前

    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

    点赞 评论 复制链接分享

相关推荐