# How to Fix Go JSON Unmarshal Type Mismatch

JSON unmarshal type mismatch errors occur when JSON data doesn't match the expected Go struct types. This guide covers diagnosis and solutions.

Error Patterns

Type Mismatch Error

text
json: cannot unmarshal string into Go struct field User.age of type int
json: cannot unmarshal number into Go struct field Config.enabled of type bool
json: cannot unmarshal array into Go struct field Data.items of type string

Unexpected JSON Structure

text
json: cannot unmarshal object into Go value of type []string
json: cannot unmarshal string into Go value of type float64

Common Causes

  1. 1.Number vs String - JSON has number, struct expects string
  2. 2.Boolean vs String - JSON has boolean, struct expects string
  3. 3.Array vs Single Value - JSON has array, struct expects single value
  4. 4.Object vs Array - JSON structure mismatch
  5. 5.Missing fields - JSON doesn't have expected fields
  6. 6.Extra fields - JSON has fields not in struct
  7. 7.Null values - JSON has null but struct field isn't pointer

Problematic Examples

```go type User struct { Name string Age int // JSON has "age": "25" (string) }

jsonStr := {"name": "John", "age": "25"} var user User err := json.Unmarshal([]byte(jsonStr), &user) // Error: cannot unmarshal string into Go struct field User.age of type int ```

```go type Config struct { Enabled bool // JSON has "enabled": "true" (string) }

jsonStr := {"enabled": "true"} var config Config err := json.Unmarshal([]byte(jsonStr), &config) // Error: cannot unmarshal string into Go struct field Config.enabled of type bool ```

Solutions

Solution 1: Accept Multiple Types with Interface

```go type FlexibleInt struct { Value int }

func (fi *FlexibleInt) UnmarshalJSON(data []byte) error { // Try as int first var i int if err := json.Unmarshal(data, &i); err == nil { fi.Value = i return nil }

// Try as string var s string if err := json.Unmarshal(data, &s); err == nil { parsed, err := strconv.Atoi(s) if err != nil { return err } fi.Value = parsed return nil }

return fmt.Errorf("cannot unmarshal FlexibleInt") }

type User struct { Name string Age FlexibleInt // Accepts int or string } ```

Solution 2: Use json.Number for Numeric Flexibility

```go type Data struct { ID json.Number // Can be string or number }

func (d *Data) GetID() (int64, error) { return d.ID.Int64() } ```

Solution 3: Custom Unmarshaler for Bool from String

```go type FlexibleBool struct { Value bool }

func (fb *FlexibleBool) UnmarshalJSON(data []byte) error { // Try as bool var b bool if err := json.Unmarshal(data, &b); err == nil { fb.Value = b return nil }

// Try as string var s string if err := json.Unmarshal(data, &s); err == nil { switch strings.ToLower(s) { case "true", "yes", "1", "on": fb.Value = true return nil case "false", "no", "0", "off": fb.Value = false return nil } return fmt.Errorf("invalid bool string: %s", s) }

return fmt.Errorf("cannot unmarshal FlexibleBool") } ```

Solution 4: Use Pointer for Optional/Null Fields

```go type User struct { Name string Age *int // Can handle null in JSON Email *string // Optional field }

jsonStr := {"name": "John", "age": null, "email": null} var user User json.Unmarshal([]byte(jsonStr), &user) // Age and Email will be nil pointers ```

Solution 5: Accept Array or Single Value

```go type FlexibleArray struct { Values []string }

func (fa *FlexibleArray) UnmarshalJSON(data []byte) error { // Try as array var arr []string if err := json.Unmarshal(data, &arr); err == nil { fa.Values = arr return nil }

// Try as single string var single string if err := json.Unmarshal(data, &single); err == nil { fa.Values = []string{single} return nil }

return fmt.Errorf("cannot unmarshal FlexibleArray") } ```

Solution 6: Use RawMessage for Delayed Parsing

```go type Message struct { Type string Data json.RawMessage // Parse later based on Type }

func handleMessage(msg Message) { switch msg.Type { case "user": var user User json.Unmarshal(msg.Data, &user) case "config": var config Config json.Unmarshal(msg.Data, &config) } } ```

Solution 7: Map for Unknown Structure

```go // Use map for flexible structure var data map[string]interface{} json.Unmarshal(jsonBytes, &data)

// Access fields dynamically if age, ok := data["age"]; ok { switch v := age.(type) { case int: fmt.Println(v) case float64: // JSON numbers are float64 fmt.Println(int(v)) case string: parsed, _ := strconv.Atoi(v) fmt.Println(parsed) } } ```

Solution 8: Strict vs Lenient Parsing

go // Lenient: Ignore unknown fields (default behavior) type Config struct { Name string json:"name"` }

// Strict: Error on unknown fields type StrictConfig struct { Name string json:"name" }

func strictUnmarshal(data []byte, v interface{}) error { dec := json.NewDecoder(bytes.NewReader(data)) dec.DisallowUnknownFields() return dec.Decode(v) } ```

Field Name Mapping

Use json Tags for Different Names

go
type User struct {
    Name     string `json:"name"`
    Age      int    `json:"age,omitempty"`      // Skip if empty
    Email    string `json:"email,omitempty"`
    FullName string `json:"full_name"`          // Snake case in JSON
    APIKey   string `json:"apiKey"`             // Camel case
}

Handle Case Sensitivity

```go // JSON field names are case-insensitive by default // But tags should match for clarity

type Data struct { ID int json:"id" // Matches "id", "ID", "Id" } ```

Debugging JSON Issues

Debug Unknown Structure

```go func debugJSON(data []byte) { var raw map[string]interface{} json.Unmarshal(data, &raw)

for key, val := range raw { fmt.Printf("%s: %T = %v\n", key, val, val) } } ```

Pretty Print JSON

go
func prettyPrintJSON(data []byte) string {
    var buf bytes.Buffer
    json.Indent(&buf, data, "", "  ")
    return buf.String()
}

Validation Pattern

go type ValidatedUser struct { Name string json:"name" Age int json:"age"` }

func (u *ValidatedUser) Validate() error { if u.Name == "" { return fmt.Errorf("name is required") } if u.Age < 0 || u.Age > 150 { return fmt.Errorf("age must be between 0 and 150") } return nil }

func parseAndValidate(data []byte) (*ValidatedUser, error) { var user ValidatedUser if err := json.Unmarshal(data, &user); err != nil { return nil, err } if err := user.Validate(); err != nil { return nil, err } return &user, nil } ```

Prevention Tips

  1. 1.Use json tags - Always specify JSON field names explicitly
  2. 2.Use pointers for optional/null fields
  3. 3.Implement custom unmarshalers for flexible types
  4. 4.Validate after unmarshal - Don't trust JSON data
  5. 5.Test with various JSON inputs - Include edge cases
  • invalid character - Malformed JSON syntax
  • unexpected end of JSON - Incomplete JSON data
  • json: unknown field - DisallowUnknownFields triggered