解组 json 时处理不同的类型
Handling different types when unmarshalling a json
我正在使用一个端点(我不拥有并且无法修复)和这个端点 returns JSON.
问题是这个 JSON 可以有不同的格式:
格式 1:
{
"message": "Message"
}
或
{
"message": ["ERROR_CODE"]
}
取决于发生的事情。
我想要一个结构来保存此响应,以便稍后我可以检查 message
是字符串还是数组,并正确地遵循流程。
可以用 Go 实现吗?我认为的第一种方法是有两个结构并尝试解码为 string
的结构,如果发生错误,则尝试解码为 array
.
的结构
有没有更优雅的方法?
将其解组为 interface{}
类型的值,并使用 type assertion or type switch 来检查其最终值的类型。请注意,默认情况下 JSON 数组被解组为 []interface{}
类型的值,因此您必须检查它以检测错误响应。
例如:
type Response struct {
Message interface{} `json:"message"`
}
func main() {
inputs := []string{
`{"message":"Message"}`,
`{"message":["ERROR_CODE"]}`,
}
for _, input := range inputs {
var r Response
if err := json.Unmarshal([]byte(input), &r); err != nil {
panic(err)
}
switch x := r.Message.(type) {
case string:
fmt.Println("Success, message:", x)
case []interface{}:
fmt.Println("Error, code:", x)
default:
fmt.Println("Something else:", x)
}
}
}
输出(在 Go Playground 上尝试):
Success, message: Message
Error, code: [ERROR_CODE]
您可以使用实现 json.Unmarshaller
的自定义类型并尝试依次解码每种可能的输入格式:
type Message struct {
Text string
Codes []int
}
func (m *Message) UnmarshalJSON(input []byte) error {
var text string
err := json.Unmarshal(input, &text)
if err == nil {
m.Text = text
m.Codes = nil
return nil
}
var codes []int
err := json.Unmarshal(input, &codes)
if err == nil {
m.Text = nil
m.Codes = codes
return nil
}
return err
}
比起解组到 interface{}
和稍后类型断言,我更喜欢这种方法,因为所有类型检查都封装在解组步骤中。
有关真实示例,请查看我的 type veryFlexibleUint64
:https://github.com/sapcc/limes/blob/fb212143c5f5b3e9272994872fcc7b758ae47646/pkg/plugins/client_ironic.go
我建议为 Message 创建一个不同的类型并制作那个工具 json.Unmarshaller
代码如下
type message struct {
Text string
Codes []string //or int , assuming array of string as it was not mentioned in the question
}
func (m *message) UnmarshalJSON(input []byte) error {
if len(input) == 0 {
return nil
}
switch input[0] {
case '"':
m.Text = strings.Trim(string(input), `"`)
return nil
case '[':
return json.Unmarshal(input, &m.Codes)
default:
return fmt.Errorf(`invalid character %q looking for " or [`, input[0])
}
}
type Error struct {
Message message `json:"message"`
}
您可能会找到带有测试的完整代码 here
我正在使用一个端点(我不拥有并且无法修复)和这个端点 returns JSON.
问题是这个 JSON 可以有不同的格式:
格式 1:
{
"message": "Message"
}
或
{
"message": ["ERROR_CODE"]
}
取决于发生的事情。
我想要一个结构来保存此响应,以便稍后我可以检查 message
是字符串还是数组,并正确地遵循流程。
可以用 Go 实现吗?我认为的第一种方法是有两个结构并尝试解码为 string
的结构,如果发生错误,则尝试解码为 array
.
有没有更优雅的方法?
将其解组为 interface{}
类型的值,并使用 type assertion or type switch 来检查其最终值的类型。请注意,默认情况下 JSON 数组被解组为 []interface{}
类型的值,因此您必须检查它以检测错误响应。
例如:
type Response struct {
Message interface{} `json:"message"`
}
func main() {
inputs := []string{
`{"message":"Message"}`,
`{"message":["ERROR_CODE"]}`,
}
for _, input := range inputs {
var r Response
if err := json.Unmarshal([]byte(input), &r); err != nil {
panic(err)
}
switch x := r.Message.(type) {
case string:
fmt.Println("Success, message:", x)
case []interface{}:
fmt.Println("Error, code:", x)
default:
fmt.Println("Something else:", x)
}
}
}
输出(在 Go Playground 上尝试):
Success, message: Message
Error, code: [ERROR_CODE]
您可以使用实现 json.Unmarshaller
的自定义类型并尝试依次解码每种可能的输入格式:
type Message struct {
Text string
Codes []int
}
func (m *Message) UnmarshalJSON(input []byte) error {
var text string
err := json.Unmarshal(input, &text)
if err == nil {
m.Text = text
m.Codes = nil
return nil
}
var codes []int
err := json.Unmarshal(input, &codes)
if err == nil {
m.Text = nil
m.Codes = codes
return nil
}
return err
}
比起解组到 interface{}
和稍后类型断言,我更喜欢这种方法,因为所有类型检查都封装在解组步骤中。
有关真实示例,请查看我的 type veryFlexibleUint64
:https://github.com/sapcc/limes/blob/fb212143c5f5b3e9272994872fcc7b758ae47646/pkg/plugins/client_ironic.go
我建议为 Message 创建一个不同的类型并制作那个工具 json.Unmarshaller
代码如下
type message struct {
Text string
Codes []string //or int , assuming array of string as it was not mentioned in the question
}
func (m *message) UnmarshalJSON(input []byte) error {
if len(input) == 0 {
return nil
}
switch input[0] {
case '"':
m.Text = strings.Trim(string(input), `"`)
return nil
case '[':
return json.Unmarshal(input, &m.Codes)
default:
return fmt.Errorf(`invalid character %q looking for " or [`, input[0])
}
}
type Error struct {
Message message `json:"message"`
}
您可能会找到带有测试的完整代码 here