将整数字段解析为字符串的JSON unmarshal操作。

huangapple go评论105阅读模式
英文:

JSON unmarshal integer field into a string

问题

我正在努力将整数反序列化为字符串结构字段。
该结构字段是一个字符串,希望能够从我的库的用户进行赋值。这就是为什么我希望它是一个字符串,因为实际上我并不关心它内部的值,只是为了将其写入数据库。
用户可以提供文本,但有些用户只赋值整数。

考虑以下结构:

type Test struct {
  Foo string
}

有时候,我得到的JSON值是有效的,但由于Foo字段是整数而不是字符串,无法反序列化为该结构:

{ "foo": "1" } // 可以
{ "foo": 1 } // 不可以

json.Unmarshal将会报错,错误信息如下:
json: cannot unmarshal number into Go struct field test.Foo of type string

请参考这个示例:https://play.golang.org/p/4Qau3umaVm

在我之前使用的其他语言的所有其他JSON库中,如果目标字段是字符串,而你得到的是一个整数,反序列化器通常会将整数包装在一个字符串中并完成操作。在Go中是否可以实现这一点?

由于我无法真正控制数据的输入方式,我需要使json.Unmarshal对此不敏感 - 另一种解决方案是将Foo定义为interface{},这会使我的代码不必要地复杂化,需要进行类型断言等操作。

有关如何实现这一点的任何想法吗?我基本上需要json:",string"的反向操作。

英文:

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

得分: 6

处理大型结构体可以使用嵌入。

更新以不丢弃可能先前设置的字段值。

func (t *T) UnmarshalJSON(d []byte) error {
    type T2 T // 创建一个与T具有相同结构但没有其方法集的新类型!
    x := struct{
        T2 // 嵌入
        Foo json.Number `json:"foo"`
    }{T2: T2(*t)} // 不要忘记这一步,如果你忘记了并且“t”已经设置了一些字段,你会丢失它们

    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

英文:

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

得分: 3

你可以通过实现json.Unmarshaler接口来自定义数据结构的反序列化方式。

处理未知类型的最简单方法是将JSON反序列化为一个中间结构,然后在反序列化过程中进行类型断言和验证:

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

英文:

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

The simplest way to handle unknown types is to unnmarshal 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

huangapple
  • 本文由 发表于 2017年8月31日 03:37:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/45968476.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定