自定义反序列化函数以将输入转换为不同的类型。

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

Custom unmarshall to a type than input out different type

问题

我正在学习Go,并且有一个问题:

我有一个Info类型的结构体,定义如下:

type Info struct {
    ID          ID     `json:"id,omitempty"`
    DisplayName string `json:"display_name,omitempty"`
}

我编写了一个自定义的UnmarshalJSON函数来解析这个结构体,因为作为输入,我可能有以下两种情况:

  • 一个[]interface{},在位置[0]上是一个int,位置1上是一个string
  • 一个布尔值,始终等于false,表示该字段为null

我希望当输入为false时,Infonil

以下是UnmarshalJSON函数的代码:

func (i *Info) UnmarshalJSON(data []byte) error {
    var v []interface{}
    if err := json.Unmarshal(data, &v); err != nil {
        var v bool
        if err = json.Unmarshal(data, &v); err != nil {
            return err
        }
        return nil
    }
    i.ID = ID(v[0].(float64))
    i.DisplayName = v[1].(string)
    return nil
}

这段代码看起来有些丑陋,我想知道是否有更好的选择。非常感谢。

英文:

I'm learning Go and I have a question:

I have an Info type that is defined like that:

type Info struct {
    ID          ID     `json:"id,omitempty"`
    DisplayName string `json:"display_name,omitempty"`
}

I made a custom UnmarshallJSON function to unmarshall this struct because as an input I have either:

  • An []interface{} with at position [0] an int and 1 a string
  • A boolean always equals to false meaning that the field is null

I want that when the input is false, the Info is nil.

Here's the UnmarshallJSON function

func (i *Info) UnmarshalJSON(data []byte) error {
var v []interface{}
if err := json.Unmarshal(data, &v); err != nil {
	var v bool
	if err = json.Unmarshal(data, &v); err != nil {
		return err
	}
	return nil
}
i.ID = ID(v[0].(float64))
i.DisplayName = v[1].(string)
return nil
}

It's ugly, and I would like to know if there's a better option.
Thank you very much.

答案1

得分: 0

首先,你应该更加谨慎地处理意外的类型和长度,以避免出现错误。然后,你可以将其解组为[]json.RawMessage,以延迟对元素的解组,直到你准备好进行解组。最后,你应该防止出现无效的true

以下是我最好的努力,请其他人随意编辑(这里是一个示例):

func (i *Info) UnmarshalJSON(data []byte) error {
    var raw interface{}
    if err := json.Unmarshal(data, &raw); err != nil {
        return err
    }

    switch r := raw.(type) {
    case []interface{}:
    case bool:
        if r {
            return errors.New("unexpected true, must be array or false")
        }
        return nil
    default:
        return fmt.Errorf("unexpected type %T, must be array or false", r)
    }

    var v []json.RawMessage
    if err := json.Unmarshal(data, &v); err != nil {
        return err
    }
    if len(v) != 2 {
        return fmt.Errorf("unexpected length %d, must be 2", len(v))
    }

    if err := json.Unmarshal(v[0], &i.ID); err != nil {
        return err
    }
    if err := json.Unmarshal(v[1], &i.DisplayName); err != nil {
        return err
    }
    return nil
}
英文:

Fist you should be more defensive about unexpected types and length to avoid a panic. Then you can unmarshal into a []json.RawMessage to defer unmarshaling of the elements until you are ready. Finally you should guard against your invalid true.

Here is my best effort, please others feel free to edit (here is a playground):

func (i *Info) UnmarshalJSON(data []byte) error {
	var raw interface{}
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	switch r := raw.(type) {
	case []interface{}:
	case bool:
		if r {
			return errors.New("unexpected true, must be array or false")
		}
		return nil
	default:
		return fmt.Errorf("unexpected type %T, must be array or false", r)
	}

	var v []json.RawMessage
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}
	if len(v) != 2 {
		return fmt.Errorf("unexpected length %d, must be 2", len(v))
	}

	if err := json.Unmarshal(v[0], &i.ID); err != nil {
		return err
	}
	if err := json.Unmarshal(v[1], &i.DisplayName); err != nil {
		return err
	}
	return nil
}

huangapple
  • 本文由 发表于 2021年6月3日 13:54:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/67816077.html
匿名

发表评论

匿名网友

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

确定