Try it yourself with our free Json Formatter tool — runs entirely in your browser, no signup needed.

How to Parse JSON in Go

How to Parse JSON in Go

Parsing JSON data is a common task in many Go applications, and the language provides a robust and efficient way to do so. In this article, we will explore how to parse JSON in Go, covering the basics, common edge cases, and performance tips.

Quick Example

Here is a minimal example that demonstrates how to parse a JSON string in Go:

package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

func main() {
	jsonStr := `{"name":"John Doe","email":"john@example.com"}`
	var person Person
	err := json.Unmarshal([]byte(jsonStr), &person)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("Name: %s, Email: %s\n", person.Name, person.Email)
}

This code defines a Person struct with two fields, Name and Email, and uses the json.Unmarshal function to parse a JSON string into a Person instance.

Step-by-Step Breakdown

Let's walk through the code line by line:

  • import "encoding/json": We import the encoding/json package, which provides the functions for encoding and decoding JSON data.
  • type Person struct { ... }: We define a Person struct with two fields, Name and Email, which will be used to hold the parsed JSON data. The json:"name" and json:"email" tags specify the JSON field names that correspond to each struct field.
  • jsonStr := {"name":"John Doe","email":"john@example.com"}`: We define a JSON string that we want to parse.
  • var person Person: We create a Person instance to hold the parsed data.
  • err := json.Unmarshal([]byte(jsonStr), &person): We use the json.Unmarshal function to parse the JSON string into the person instance. The []byte(jsonStr) conversion is necessary because json.Unmarshal expects a byte slice as input.
  • if err != nil { ... }: We check if the parsing failed, and if so, print the error message and return.
  • fmt.Printf("Name: %s, Email: %s\n", person.Name, person.Email): If the parsing succeeds, we print the parsed data to the console.

Handling Edge Cases

Here are some common edge cases to consider when parsing JSON in Go:

Empty/Null Input

If the input JSON string is empty or null, the json.Unmarshal function will return an error. We can handle this case by checking for an error and returning a default value or an error message:

jsonStr := ""
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)
if err != nil {
	fmt.Println("Error: empty input")
	return
}

Invalid Input

If the input JSON string is invalid (e.g., malformed or contains unknown fields), the json.Unmarshal function will return an error. We can handle this case by checking for an error and returning an error message:

jsonStr := `{"name":"John Doe","email":"john@example.com"," invalidField":"value"}`
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)
if err != nil {
	fmt.Println("Error: invalid input")
	return
}

Large Input

If the input JSON string is very large, we may need to consider using a streaming JSON parser to avoid loading the entire string into memory at once. Go provides the json.Decoder type for this purpose:

jsonStr := `...large JSON string...`
decoder := json.NewDecoder(strings.NewReader(jsonStr))
var person Person
err := decoder.Decode(&person)
if err != nil {
	fmt.Println(err)
	return
}

Unicode/Special Characters

Go's JSON parser handles Unicode and special characters correctly, but we may need to take care when printing or processing the parsed data. For example, we may need to use the fmt.Printf function with the %q format specifier to print strings with special characters:

jsonStr := `{"name":"John Doe","email":"john@example.com","specialField":"\u2028"}`
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)
if err != nil {
	fmt.Println(err)
	return
}
fmt.Printf("Name: %q, Email: %q\n", person.Name, person.Email)

Common Mistakes

Here are three common mistakes developers make when parsing JSON in Go, along with the correct solutions:

Mistake 1: Forgetting to check for errors

// Wrong
var person Person
json.Unmarshal([]byte(jsonStr), &person)
fmt.Println(person.Name)

// Correct
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)
if err != nil {
	fmt.Println(err)
	return
}
fmt.Println(person.Name)

Mistake 2: Using the wrong struct type

// Wrong
type Person struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}
var person map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &person)

// Correct
type Person struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)

Mistake 3: Not handling unknown fields

// Wrong
type Person struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)

// Correct
type Person struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)
if err != nil {
	fmt.Println(err)
	return
}

Performance Tips

Here are three performance tips for parsing JSON in Go:

  1. Use a byte slice instead of a string: When parsing JSON, it's more efficient to use a byte slice instead of a string, since the json.Unmarshal function expects a byte slice as input.
jsonStr := `{"name":"John Doe","email":"john@example.com"}`
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)
  1. Use a struct with tagged fields: When defining a struct to hold the parsed JSON data, use tagged fields to specify the JSON field names. This allows the json.Unmarshal function to efficiently match the JSON fields to the struct fields.
type Person struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}
  1. Avoid using map[string]interface{}: When parsing JSON, avoid using a map[string]interface{} to hold the parsed data, since this can lead to slow performance and memory usage. Instead, define a struct with tagged fields to hold the parsed data.
// Slow
var person map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &person)

// Fast
type Person struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)

FAQ

Q: What is the difference between json.Unmarshal and json.NewDecoder?

A: json.Unmarshal is a function that parses a JSON string into a Go value, while json.NewDecoder is a type that provides a streaming JSON parser.

Q: How do I handle unknown fields in my JSON input?

A: You can handle unknown fields by using the json:",omitempty" tag on your struct fields, or by using a map[string]interface{} to hold the parsed data.

Q: Can I use json.Unmarshal with a large JSON input?

A: Yes, but you may need to consider using a streaming JSON parser like json.NewDecoder to avoid loading the entire input into memory at once.

Q: How do I handle Unicode and special characters in my JSON input?

A: Go's JSON parser handles Unicode and special characters correctly, but you may need to take care when printing or processing the parsed data.

Q: What is the performance impact of using map[string]interface{} to hold parsed JSON data?

A: Using map[string]interface{} to hold parsed JSON data can lead to slow performance and memory usage. Instead, define a struct with tagged fields to hold the parsed data.

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp