Golang中非本地类型JSON解码:字符串转整数及无标签映射问题
Hey there! Let's tackle your two Go JSON decoding issues one by one, with clear explanations and actionable solutions.
Why This Happens
Go's standard encoding/json package is strict about type matching out of the box. If your JSON has a string like "80" for the Port field, but your struct defines Port as an int, the decoder will throw an error—it won't automatically convert the string to an integer. Only when the JSON value is a raw number (like 80) will it work smoothly.
The Fix: Custom UnmarshalJSON Method
We can override the default decoding behavior by implementing a custom UnmarshalJSON method for your struct. Let's use a simplified version of your ELBV2 TargetGroup struct as an example:
import ( "encoding/json" "fmt" "strconv" ) type TargetGroup struct { Port int // Add your other required fields here }
Now add the custom decoding logic:
func (tg *TargetGroup) UnmarshalJSON(data []byte) error { // First, define a temporary struct that mirrors your original, but uses string for Port type tempTargetGroup struct { Port string `json:"Port"` // Copy all other fields from your TargetGroup here, keeping their types } var temp tempTargetGroup // Decode the JSON into our temporary struct first if err := json.Unmarshal(data, &temp); err != nil { return fmt.Errorf("failed to parse JSON: %w", err) } // Convert the string Port to an integer portValue, err := strconv.Atoi(temp.Port) if err != nil { return fmt.Errorf("invalid Port value '%s': must be a numeric string", temp.Port) } tg.Port = portValue // Copy over values for other fields (e.g., tg.ARN = temp.ARN) return nil }
This method lets you handle both string-formatted and raw numeric values for Port seamlessly.
Why This Happens
By default, the Go JSON decoder ignores any fields in the JSON that don't match an exported field (capitalized) in your struct. If you're seeing arbitrary fields like Name mapping to your struct, that means your struct actually has an exported Name field—without a json tag, the decoder uses the exact field name (case-sensitive) to match JSON keys. If you want to block any fields that aren't explicitly defined in your struct, or control exactly which JSON keys map to your struct fields, you need to adjust the decoder behavior.
Fix 1: Reject Unknown Fields with DisallowUnknownFields()
If you want the decoder to throw an error when it encounters a field that's not defined in your struct, use the DisallowUnknownFields() option on a json.Decoder:
import ( "bytes" "encoding/json" "fmt" ) func main() { jsonInput := []byte(`{"Port": "80", "Name": "unwanted-field"}`) var tg TargetGroup decoder := json.NewDecoder(bytes.NewReader(jsonInput)) // Enable strict mode: reject any fields not in the struct decoder.DisallowUnknownFields() if err := decoder.Decode(&tg); err != nil { fmt.Printf("Decoding failed: %v\n", err) // This will error with: json: unknown field "Name" } }
Fix 2: Use json Tags to Explicitly Control Field Mapping
If you want to specify exactly which JSON keys map to your struct fields (e.g., allow port lowercase instead of Port), add json tags to your struct fields. This also makes it clear which fields are intended to be decoded:
type TargetGroup struct { Port int `json:"port"` // Only matches the "port" key in JSON // Add tags for other fields like: // ARN string `json:"arn"` }
With tags set, only JSON keys that match the tag value will be mapped to your struct fields. Combine this with DisallowUnknownFields() for strict control over what gets decoded.
内容的提问来源于stack exchange,提问作者k_vishwanath

