在Go中解码变量模式的JSON

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

Decoding variable-schema JSON in Go

问题

我正在询问关于Go的encoding/json,但我猜它也适用于将JSON块映射到任何其他语言中的对象的任何其他JSON库。

这是一个例子。如果您想使用goo.gl URL缩短器API缩短URL,您将得到一个成功的响应:

{
 "kind": "urlshortener#url",
 "id": "http://goo.gl/fbsS",
 "longUrl": "http://www.google.com/"
}

或者一个错误响应:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "required",
    "message": "Required",
    "locationType": "parameter",
    "location": "resource.longUrl"
   }
  ],
  "code": 400,
  "message": "Required"
 }
}

有没有一种处理这种情况的惯用方法 - 一个可以遵循两个完全不同模式的响应?

通常我使用映射/列表处理JSON;我知道在Go中这是可能的。我可以解组到map[string]interface{},然后检查映射是否具有"error"作为键。但是然后我可能需要再次解码为适当的struct,我想。 (我错了吗?)

我正在做类似这样的事情。我为每种响应类型都有一个类型:

type successResponse struct {
    Kind string
    Id string
    LongUrl string
}

type errorResponse struct {
    Error struct {
        Errors []struct {
            Domain string
            Reason string
            Message string
            LocationType string
            Location string
        }
        Code int
        Message string
    }
}

解码看起来像这样:

s := new(successResponse)
err := json.Unmarshal(blob, s)
if err == nil {
    // 处理成功
} else {
    e := new(errorResponse)
    err = json.Unmarshal(blob, e)
    if err == nil {
        // 处理错误响应
    } else {
        // 处理实际错误
    }
}

但是这看起来有点丑陋。我应该如何处理这个问题?

英文:

I'm asking this about Go's encoding/json, but I guess it also applies to any other JSON libraries that map JSON blobs to objects in whatever language.

Here's an example. If you want to a shorten a URL using the goo.gl URL shortener API, you get back either a successful response:

{
 "kind": "urlshortener#url",
 "id": "http://goo.gl/fbsS",
 "longUrl": "http://www.google.com/"
}

Or an error response:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "required",
    "message": "Required",
    "locationType": "parameter",
    "location": "resource.longUrl"
   }
  ],
  "code": 400,
  "message": "Required"
 }
}

Is there an idiomatic way of dealing with this -- a response that could adhere to two completely different schemas?

Normally I deal with JSON using maps/lists; I know that's possible in Go. I could unmarshal to a map[string]interface{} and then check if the map has "error" as a key. But then I'd have to decode again into a proper struct, I think. (Am I wrong?)

I'm doing something like this. I have one type for each kind of response:

type successResponse struct {
    Kind string
    Id string
    LongUrl string
}

type errorResponse struct {
    Error struct {
        Errors []struct {
            Domain string
            Reason string
            Message string
            LocationType string
            Location string
        }
        Code int
        Message string
    }
}

And decoding looks like this:

s := new(successResponse)
err := json.Unmarshal(blob, s)
if err == nil {
    // handle success
} else {
    e := new(errorResponse)
    err = json.Unmarshal(blob, e)
    if err == nil {
        // handle error response
    } else {
        // handle actual error
    }
}

But that seems kind of ugly. How should I approach this?

答案1

得分: 6

由于您的JSON响应中的字段彼此不同,您可以创建一个包含所有字段联合的结构体。JSON解码器将忽略JSON字符串中不存在的字段,您可以测试字段的存在来确定返回的响应类型。

英文:

Since the fields in your json responses are distinct from each other you can just create one struct with the union of all the fields. The json decoder will ignore fields that are not present in the json string and you can test the existence of the fields to know which type of response you are getting back.

答案2

得分: 6

我也对此感到困惑,并且以为我需要再次解码它。但实际上不需要。你只需要将interface{}数据强制转换为适当的结构。

例如,如果json包将值放入了通用的interface{}中,你可以使用error := val.(ErrorType)将其转换为ErrorType

如果你根据值的类型进行解析,你可以在switch语句中使用foo.(type)来“做正确的事情”。

我只学习Go语言一周,所以代码不是很漂亮,但在geodns JSON配置解析中有一些示例。

英文:

I was confused about this, too, and thought I had to decode it again. You don't, though. You just have to typecast the interface{} data into the appropriate structure.

For example if the json package has put the value into a generic interface{}, you can typecast it into ErrorType with error := val.(ErrorType).

You can use foo.(type) in a switch statement to "do the right thing", if you are parsing based on what type the value is.

I've only been learning Go this week so it's not the prettiest code, but there are some examples in the geodns JSON configuration parsing.

答案3

得分: 5

你试过Go-SimpleJSON吗?我认为这可能会解决你的问题。

英文:

Have you tried Go-SimpleJSON? I think this might solve your issue.

答案4

得分: 3

type Response struct {
Kind string
Id string
LongUrl string
Error struct {
Errors []struct {
Domain string
Reason string
Message string
LocationType string
Location string
}
Code int
Message string
}
}

s := Response{}
if err := json.Unmarshal(blob, &s); err == nil {
if s.Error == nil {
// success
} else {
// error
}
} else {
// something went wrong
}

英文:
type Response struct {
    Kind    string
    Id      string
    LongUrl string
    Error   struct {
        Errors []struct {
            Domain       string
            Reason       string
            Message      string
            LocationType string
            Location     string
        }
        Code    int
        Message string
    }
}

s := Response{}
if err := json.Unmarshal(blob, &s); err == nil {
    if s.Error == nil {
        // success
    } else {
        // error
    }
} else {
    // something went wrong
}

huangapple
  • 本文由 发表于 2012年8月24日 04:28:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/12099470.html
匿名

发表评论

匿名网友

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

确定