Golang中特定于平台的反序列化?

I am hitting a REST API and getting some data back. I came across an interesting behaviour yesterday. I haven't understood the exact reasoning behind the it yet. Which is what I'm trying to seek here. For a payload that looks like -

{
    "id": 2091967,
    "first_name": "",
    "last_name": "",
    "email": "",
    "telephone": "",
    "timezone": "",
    "weekly_capacity": "",
    "has_access_to_all_future_projects": false,
    "is_contractor": false,
    "is_admin": false,
    "is_project_manager": false,
    "can_see_rates": false,
    "can_create_projects": false,
    "can_create_invoices": false,
    "is_active": false,
    "created_at": "2018-04-16T00:48:30Z",
    "updated_at": "2018-11-07T22:47:43Z",
    "default_hourly_rate": null,
    "cost_rate": null,
    "roles": [
        "blah"
    ],
    "avatar_url": ""
}

I used a function like below to get the email -

func GetUserEmail(userID int) string {
    resp := getFromSomething("https://something/users/" + strconv.Itoa(userID))
    var result map[string]string

    json.NewDecoder(resp.Body).Decode(&result)
    log.Printf("userEmail: %s", result["email"])
    return result["email"]
}

The code worked perfectly on my mac where I was building it like - env GOOS=linux go build -ldflags="-s -w" -o bin/something cmd/main.go But, it failed to deserialize, and didn't print anything on an EC2 instance when using the same build command.

But then, I changed the var result map[string]string to var result map[string]interface{} and it worked on both my EC2 instance and mac.

I also had to typecast the interface{} object at the end before returning it.

func GetUserEmail(userID int) string {
    resp := getFromSomething("https://something/users/" + strconv.Itoa(userID))
    var result map[string]interface{}

    json.NewDecoder(resp.Body).Decode(&result)
    log.Printf("userEmail: %s", result["email"])
    return result["email"].(string)
}

Has anyone seen things like this before? Or, does anyone know why this happened?

I understand that the payload can always be represented better by var result map[string]interface{}, but my question is - why did that earlier representation of var result map[string]string work on the Mac and not on EC2?

Go version on Mac - go version go1.11.2 darwin/amd64 and that on EC2 is go version go1.10.3 linux/amd64

Always check for and handle errors.

The error returned from Decode explains the problem. The application is attempting to decode numbers, booleans and arrays to string values.

var v map[string]string
err := json.NewDecoder(data).Decode(&v) // data is the JSON document from the question
fmt.Println(err)  // prints json: cannot unmarshal number into Go value of type string

Run it on the Playground.

This issue is not platform specific. I have a couple of guesses why you saw different results:

  • Different JSON documents were used when testing on the different platforms.
  • The question states that the command env GOOS=linux go build -ldflags="-s -w" -o bin/something cmd/main.go was used to build the Mac version, but this command does not build a binary that's executable on the Mac. Perhaps you were not running the code you thought you were running.

Either decode to map[string]interface{} as you discovered or decode to a struct with the one field you want:

var v struct{ Email string }
if err := json.NewDecoder(data).Decode(&v); err != nil {
    // handle error
}
fmt.Println(v.Email) // prints the decoded email value.

Run it on the Playground.