10

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://www.google.com/",
 "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?

4
  • I'm not familiar with the goo.gl API but could you check the HTTP response code to decide which structure to use? Not the code in the JSON itself, but the code that comes at the beginning of the HTTP response (Response.StatusCode : golang.org/pkg/net/http/#Response) Commented Aug 23, 2012 at 21:10
  • Yes, I hadn't realized that... I'm not sure if this question still makes sense. Are there many cases where you're not sure about the schema of incoming JSON, but you know it could be one of a few things? If not, feel free to post that as an answer :s Commented Aug 23, 2012 at 21:24
  • It addresses your particular situation, but the question would still apply to other API providers who might return a 200 status code for every response (successes and errors). I don't have an answer for that =) Commented Aug 23, 2012 at 23:31
  • 1
    FYI there is an official Go client library for this API at code-google-com/p/google-api-go-client Commented Aug 26, 2012 at 3:02

4 Answers 4

6

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.

Sign up to request clarification or add additional context in comments.

Comments

6

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.

Comments

5

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

1 Comment

Thanks you for the link. This is exactly the lib that I was missing for being able to work with generic json. This is very helpful when you can't expect to have a struct for every possible response from an API.
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
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.