有条件地解组JSON的干净方法

I'm sending requests to a JSON API, and it either returns an error...

{
  "error": {
    "code": 404,
    "message": "Document not found.",
    "status": "NOT_FOUND"
  }
}

or the data.

{
  "name": "projectname",
  "fields": {
    "userId": {
      "stringValue": "erw9384rjidfge"
    }
  },
  "createTime": "2018-06-28T00:52:25.638791Z",
  "updateTime": "2018-06-28T00:52:25.638791Z"
}

Here are the corresponding structs

type HttpError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Status  string `json:"status"`
}

type Document struct {
    Name   string `json:"name"`
    Fields struct {
        UserID struct {
            StringValue string `json:"stringValue"`
        } `json:"userId"`
    } `json:"fields"`
    CreateTime time.Time `json:"createTime"`
    UpdateTime time.Time `json:"updateTime"`
}

Once I get the response, how do I cleanly/concisely unmarshal to the correct struct? I've seen a lot of ugly solutions (maybe Go's fault instead of the writers).

func getDocument() {
    resp, _ := httpClient.Get("example.com")
    defer resp.Body.Close()
    bodyBytes, _ := ioutil.ReadAll(resp.Body)

    var data map[string]interface{}
    // How to unmarshal to either HttpError or Document??
    err = json.Unmarshal([]byte(bodyBytes), &data)

}

By the way, I can't use the Go Firestore client library because reasons.

You can use an struct type inside your unmarshal method; with pointers to establish what's been unmarshalled.

Note: This code assumes there is no overlap of top level json keys... error / name / fields / etc.

type outer struct {
    *HttpError `json:"error"`
    *Document
}

var out outer

if err := json.Unmarshal(bodyBytes, &out); err != nil {
    // error handling
}

if out.HttpErr != nil {
    // handle error json case
}

// Here you can use out.Document, probably worth check if it is nil first.

Runnable example