如何在解析 JSON 时适应两种不同类型的相同字段?

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

How to accommodate for 2 different types of the same field when parsing JSON

问题

我正在使用一个JSON REST API,它可以返回相同字段的两种不同类型。

这是API的一个示例响应:

"result": {
	"value": {
		"error": "Invalid data",
		"logs": []
	}
},

但有时它也会返回以下内容:

"result": {
	"value": {
		"error": {
             "msg": "Rate limit exceeded"
           },
		"logs": []
	}
},

如你所见,error字段可以变成一个对象。

以下是我在代码中定义的类型:

type ApiResponse struct {
	Result struct {
		Value struct {
			Error string `json:"error"`
			Logs []string `json:"logs"`
		} `json:"value"`
	} `json:"result"`
}

但由于error的类型可能会变化,这在JSON解组时有时会引发问题:json: cannot unmarshal object into Go struct field .result.value.error of type string

我该如何处理这种情况?

英文:

I'm working with a JSON rest api that can return 2 different types of the same field.

Here's an example response from the api:

"result": {
	"value": {
		"error": "Invalid data",
		"logs": [],
	}
},

But sometimes it also returns this

"result": {
	"value": {
		"error": {
             "msg": "Rate limit exceeded"
           },
		"logs": []
	}
},

As you can see the error field can change to an object.

Here's how i have defined the type in my code

type ApiResponse struct {
	Result struct {
		Value struct {
			Error string `json:"error"`
			Logs []string `json:"logs"`
		} `json:"value"`
	} `json:"result"`
}

But since the type of error can change, this sometimes causes issues when JSON unmarshaling: json: cannot unmarshal object into Go struct field .result.value.error of type string

How can i deal with this scenario?

答案1

得分: 4

你可以实现json.Unmarshaler接口来自定义如何解码错误字段。

type ErrorMessage struct {
	Text string
}

func (e *ErrorMessage) UnmarshalJSON(data []byte) error {
	if len(data) == 0 || string(data) == "null" {
		return nil
	}
	if data[0] == '"' && data[len(data)-1] == '"' { // 字符串?
		return json.Unmarshal(data, &e.Text)
	}
	if data[0] == '{' && data[len(data)-1] == '}' { // 对象?
		type T struct {
			Text string `json:"msg"`
		}
		return json.Unmarshal(data, (*T)(e))
	}
	return fmt.Errorf("不支持的错误消息类型")
}

然后将ApiResponse类型的Error字段更新为ErrorMessage类型。

type ApiResponse struct {
	Result struct {
		Value struct {
			Error ErrorMessage `json:"error"`
			Logs  []string     `json:"logs"`
		} `json:"value"`
	} `json:"result"`
}

https://go.dev/play/p/U7FBiA9qB54

英文:

You can implement the json.Unmarshaler interface to customize how the error field will be decoded.

type ErrorMessage struct {
	Text string
}

func (e *ErrorMessage) UnmarshalJSON(data []byte) error {
	if len(data) == 0 || string(data) == "null" {
		return nil
	}
	if data[0] == '"' && data[len(data)-1] == '"' { // string?
		return json.Unmarshal(data, &e.Text)
	}
	if data[0] == '{' && data[len(data)-1] == '}' { // object?
		type T struct {
			Text string `json:"msg"`
		}
		return json.Unmarshal(data, (*T)(e))
	}
	return fmt.Errorf("unsupported error message type")
}

Then update the ApiResponse type's Error field to be of type ErrorMessage.

type ApiResponse struct {
	Result struct {
		Value struct {
			Error ErrorMessage `json:"error"`
			Logs  []string     `json:"logs"`
		} `json:"value"`
	} `json:"result"`
}

https://go.dev/play/p/U7FBiA9qB54

答案2

得分: 0

由于接口Error被定义为字符串,你需要一个实现了Error接口的自定义类型错误对象,该对象可能返回msg。然后创建自己的解组方法来处理可能的JSON字符串。

英文:

As interface Error is defined as string, you need a custom type error object implementing the Error interface - which returns msg probably. Then create your own unmarshalling method which handles the possible json strings.

答案3

得分: 0

将您的结构定义如下:

type ApiResponse struct {
    Result struct {
        Value struct {
            Error interface{} `json:"error"`
            Logs  []string   `json:"logs"`
        } `json:"value"`
    } `json:"result"`
}

将其解组为类型为ApiResponse的变量(假设为data)。然后可以按如下方式检查Error的类型:

switch v := data.Result.Value.Error.(type) {
    case string:
        // 第一种情况
    case map[string]interface{}:
        // 第二种情况,读取键"msg"
    default:
        fmt.Printf("我不知道类型 %T!\n", v)
}
英文:

Define your structure as follows:

type ApiResponse struct {
    Result struct {
        Value struct {
            Error interface{}`json:"error"`
            Logs  []string   `json:"logs"`
        } `json:"value"`
    } `json:"result"`
}

Unmarshall into a variable of type ApiResponse (lets assume data). The type of Error then can be checked as follows:

switch v := data.Result.Value.Error.(type) {
	case string:
		// 1st case
	case map[string]interface{}:
		// 2nd case, read key "msg"
	default:
		fmt.Printf("I don't know about type %T!\n", v)
}

huangapple
  • 本文由 发表于 2022年5月3日 13:59:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/72095328.html
匿名

发表评论

匿名网友

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

确定