Golang中Json.unmarshal解析未知响应体时如何处理

Golang中Json.unmarshal解析未知响应体时如何处理 我调用一个API,而同一个API会返回两种不同的响应结构(与文档描述不符……API Reference — Kea 2.0.1 documentation

[ { "result": 0, "text": "Configuration successful." } ]

我定义了以下结构体来解析响应体:

type firstResponse []struct {
	Result int    `json:"result"`
	Text   string `json:"text"`
}

{ "result": 400, "text": "Bad Request" }

我也定义了相应的结构体来解析响应体:

type secondResponse struct {
	Result int    `json:"result"`
	Text   string `json:"text"`
}

处理这种情况的最佳方式是什么?如果能提供一个简单的答案,为我指明正确的方向,我将不胜感激

此外,当我需要访问这些数据时,我还必须知道我使用了哪种结构体类型。


更多关于Golang中Json.unmarshal解析未知响应体时如何处理的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

好的,我会在IRC上向他们提一下这件事。

更多关于Golang中Json.unmarshal解析未知响应体时如何处理的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


啊,是的,幸运的是,“错误请求”会返回400状态码,而成功请求则返回200,这样我就能保持代码的整洁。

顺便提一下……API的代码返回了两种不同类型的结构体,这会不会是一个bug呢?

检查HTTP状态码。如果是400,你很可能在响应体中得到了第二个JSON。

我还没有查看你链接的文档,但可悲的是,许多API文档只描述了“成功”的情况,错误处理通常只是在一个段落中简要描述,将所有可能的情况合并成一句话和一个几乎不包含实际价值的JSON对象……

再次,你是否检查了响应的HTTP状态码?如果它与JSON中的代码相符,那么基于此进行分支判断应该很容易。

另一种方法是先将其解组到 interface{} 中,然后使用类型开关来区分顶层对象和顶层列表。接着再将 interface{} 值转换为结构体。不过,这样代码会非常冗长。

func main() {
    fmt.Println("hello world")
}

这取决于它们的文档。

一般来说,API 是无类型的。是人类为二进制数据块赋予了一些语义和句法意义。

它们提供的文档描述了它们那部分的“契约”。

如果它们的做法与文档描述的不同,那么要么是结果错了,要么是文档错了。

我没有也不会阅读链接的文档,因为它内容太多了。

不过正如我已经说过的,“一切顺利”通常作为端点文档的一部分进行描述,而“出现错误”则有自己通用的章节,遗憾的是这部分内容通常非常简略。

感谢您的回答……但我实际面临的问题是处理响应,因为它们是两种不同的结构,我不知道会返回哪一种。

我正在寻找处理它们的方法。我想我可以:

  • 使用一系列错误检查来填充正确的结构
  • 剥离数组(如果存在),并且有办法做到这一点并将结果放入secondResponse
  • 添加一个数组(如果不存在),并且有办法做到这一点并将结果放入firstResponse
  • 将响应转换为字符串,添加或移除数组,然后转换为所需的结构

只是想请教是否有更好的方法。 我查阅了 Go 语言的 JSON 文档,但没有发现什么有用的信息。

在Golang中处理JSON响应结构不确定的情况,可以使用以下两种方法:

方法1:使用json.RawMessage进行延迟解析

package main

import (
    "encoding/json"
    "fmt"
)

// 定义两种可能的结构
type FirstResponse []struct {
    Result int    `json:"result"`
    Text   string `json:"text"`
}

type SecondResponse struct {
    Result int    `json:"result"`
    Text   string `json:"text"`
}

func parseResponse(data []byte) (interface{}, error) {
    // 先尝试解析为数组格式
    var firstResp FirstResponse
    if err := json.Unmarshal(data, &firstResp); err == nil && len(firstResp) > 0 {
        return firstResp, nil
    }
    
    // 再尝试解析为对象格式
    var secondResp SecondResponse
    if err := json.Unmarshal(data, &secondResp); err == nil {
        return secondResp, nil
    }
    
    return nil, fmt.Errorf("无法解析响应")
}

func main() {
    // 测试数据
    data1 := []byte(`[ { "result": 0, "text": "Configuration successful." } ]`)
    data2 := []byte(`{ "result": 400, "text": "Bad Request" }`)
    
    // 解析响应1
    resp1, _ := parseResponse(data1)
    switch v := resp1.(type) {
    case FirstResponse:
        fmt.Printf("类型: FirstResponse, 结果: %v\n", v[0].Text)
    case SecondResponse:
        fmt.Printf("类型: SecondResponse, 结果: %v\n", v.Text)
    }
    
    // 解析响应2
    resp2, _ := parseResponse(data2)
    switch v := resp2.(type) {
    case FirstResponse:
        fmt.Printf("类型: FirstResponse, 结果: %v\n", v[0].Text)
    case SecondResponse:
        fmt.Printf("类型: SecondResponse, 结果: %v\n", v.Text)
    }
}

方法2:使用自定义UnmarshalJSON方法

package main

import (
    "encoding/json"
    "fmt"
)

type UnifiedResponse struct {
    IsArray bool
    Array    []ResponseItem
    Single   ResponseItem
}

type ResponseItem struct {
    Result int    `json:"result"`
    Text   string `json:"text"`
}

func (ur *UnifiedResponse) UnmarshalJSON(data []byte) error {
    // 尝试解析为数组
    var arr []ResponseItem
    if err := json.Unmarshal(data, &arr); err == nil {
        ur.IsArray = true
        ur.Array = arr
        return nil
    }
    
    // 尝试解析为单个对象
    var single ResponseItem
    if err := json.Unmarshal(data, &single); err == nil {
        ur.IsArray = false
        ur.Single = single
        return nil
    }
    
    return fmt.Errorf("无法解析JSON")
}

func (ur *UnifiedResponse) GetResult() int {
    if ur.IsArray && len(ur.Array) > 0 {
        return ur.Array[0].Result
    }
    return ur.Single.Result
}

func (ur *UnifiedResponse) GetText() string {
    if ur.IsArray && len(ur.Array) > 0 {
        return ur.Array[0].Text
    }
    return ur.Single.Text
}

func main() {
    data1 := []byte(`[ { "result": 0, "text": "Configuration successful." } ]`)
    data2 := []byte(`{ "result": 400, "text": "Bad Request" }`)
    
    var resp1 UnifiedResponse
    json.Unmarshal(data1, &resp1)
    fmt.Printf("响应1 - 类型: %v, 文本: %s\n", resp1.IsArray, resp1.GetText())
    
    var resp2 UnifiedResponse
    json.Unmarshal(data2, &resp2)
    fmt.Printf("响应2 - 类型: %v, 文本: %s\n", resp2.IsArray, resp2.GetText())
}

方法3:使用interface{}和类型断言

package main

import (
    "encoding/json"
    "fmt"
)

func parseAPIResponse(data []byte) {
    var result interface{}
    json.Unmarshal(data, &result)
    
    switch v := result.(type) {
    case []interface{}:
        // 数组格式
        if len(v) > 0 {
            if item, ok := v[0].(map[string]interface{}); ok {
                fmt.Printf("数组格式 - Result: %v, Text: %v\n", 
                    item["result"], item["text"])
            }
        }
    case map[string]interface{}:
        // 对象格式
        fmt.Printf("对象格式 - Result: %v, Text: %v\n", 
            v["result"], v["text"])
    }
}

func main() {
    data1 := []byte(`[ { "result": 0, "text": "Configuration successful." } ]`)
    data2 := []byte(`{ "result": 400, "text": "Bad Request" }`)
    
    parseAPIResponse(data1)
    parseAPIResponse(data2)
}

推荐使用方法2,它提供了类型安全的访问方式,同时保持了代码的清晰性。通过统一的接口访问数据,无需在业务代码中频繁进行类型判断。

回到顶部