# JSON

Course: [Go](https://www.devtutorials.ai/learn/go) · Section: Files and JSON
Source: https://www.devtutorials.ai/learn/go/files-and-json/json

> JSON in Go with encoding/json: JSON structure, json.Marshal and MarshalIndent, json.Unmarshal, struct tags, nested JSON, and uses like config files and API responses.

## What Is JSON?

**JSON** (JavaScript Object Notation) = a simple text format for data, made of names and values.

```text
{"name": "Asha", "class": "8A", "marks": 91}
```

- People can read it.
- Almost every language and website can read and write it.
- Go handles it with the `encoding/json` package.

**Simple lesson:** JSON is the common text language that programs use to share data.

### A Real-World Example

Your school sends report cards to parents' phone apps.

- The school's server is written in Go.
- The phone app is written in another language.
- They cannot share Go structs directly.
- So the server turns each report card into **JSON text**, and the app reads that text.

Now the mapping:

- **Struct → JSON text** = **encoding** (also called marshaling).
- **JSON text → struct** = **decoding** (also called unmarshaling).

Go reads struct **tags** to name JSON keys. You met tags in **Structs and Methods**.

Only **exported** fields (capital first letter) are written to JSON or read from it. See **Encapsulation**.

## What You Will Learn

In this lesson, you will learn:

1. What JSON is: its structure, objects and arrays.
2. How to encode Go values with `json.Marshal()` and `json.MarshalIndent()`.
3. How to decode JSON with `json.Unmarshal()`.
4. How to go from struct to JSON and back, use tags and handle nested JSON.
5. Common uses: config files, API responses, local storage and data exchange.

## JSON Fundamentals

### JSON Structure

JSON has six kinds of values:

| JSON | Example | Go type |
|---|---|---|
| string | `"Asha"` | `string` |
| number | `91`, `3.5` | `int`, `float64` |
| boolean | `true` | `bool` |
| null | `null` | `nil` |
| object | `{"a": 1}` | struct or `map[string]T` |
| array | `[1, 2, 3]` | slice |

### JSON Objects

**JSON object** = name-value pairs inside `{ }`.

- Names are always in **double quotes**.
- A colon `:` separates name and value.
- Commas separate the pairs. No comma after the last one.

### JSON Arrays

**JSON array** = a list of values inside `[ ]`.

```text
{
  "class": "8A",
  "students": ["Asha", "Meena"],
  "teacher": {"name": "Mrs. Iyer", "subject": "Maths"}
}
```

- Values can be nested: an object inside an object, an array inside an object.

**Simple lesson:** JSON is built from objects `{ }`, arrays `[ ]`, strings, numbers, booleans and `null`.

## Encoding JSON

**Encode** (marshal) = turn a Go value into JSON text.

### json.Marshal()

```go runnable title="Struct to JSON"
package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name  string
	Class string
	Marks int
}

func main() {
	s := Student{Name: "Asha", Class: "8A", Marks: 91}
	data, err := json.Marshal(s)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(data))
}
```

```text
{"Name":"Asha","Class":"8A","Marks":91}
```

- `json.Marshal` returns `[]byte` and an error.
- With no tags, the keys are the Go field names.

### json.MarshalIndent()

**MarshalIndent** = the same, but spread over lines, easy to read.

```go runnable title="Pretty JSON"
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	timetable := map[string][]string{
		"Monday":  {"Maths", "English"},
		"Tuesday": {"Science"},
	}
	data, _ := json.MarshalIndent(timetable, "", "  ")
	fmt.Println(string(data))
}
```

```text
{
  "Monday": [
    "Maths",
    "English"
  ],
  "Tuesday": [
    "Science"
  ]
}
```

- The second argument is a prefix for each line (usually `""`).
- The third is the indent: two spaces here.
- Map keys come out sorted.

**Simple lesson:** `json.Marshal` turns Go values into JSON text, and `MarshalIndent` makes it pretty.

## Decoding JSON

**Decode** (unmarshal) = read JSON text into a Go value.

### json.Unmarshal()

Press **Play** to watch JSON fill a struct.

```go runnable title="Decode into a struct"
package main

import (
	"encoding/json"
	"fmt"
)

type Book struct {
	Title string
	Pages int
}

func main() {
	text := []byte(`{"Title":"Malgudi Days","Pages":256}`)
	var b Book
	if err := json.Unmarshal(text, &b); err != nil {
		fmt.Println("bad JSON:", err)
		return
	}
	fmt.Println(b.Title, "has", b.Pages, "pages")
}
```

```text
Malgudi Days has 256 pages
```

- Pass a **pointer**: `&b`. See **Pointers**.
- Unknown JSON keys are ignored.
- Missing keys leave the field at its zero value.

### Bad JSON

```go runnable title="Broken JSON gives an error"
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	var m map[string]int
	err := json.Unmarshal([]byte(`{"maths": 91,}`), &m)
	fmt.Println(err != nil)
	fmt.Println(err)
}
```

```text
true
invalid character '}' looking for beginning of object key string
```

- The trailing comma is not allowed in JSON.
- A wrong type, like text where a number is expected, also gives an error.

### Decoding into a Map

When you do not know the keys in advance, decode into `map[string]any`.

```go runnable title="Unknown shape: map[string]any"
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	text := []byte(`{"name":"Meena","marks":85,"clubs":["art","chess"]}`)
	var data map[string]any
	json.Unmarshal(text, &data)
	fmt.Println(data["name"], data["marks"], data["clubs"])
	marks := data["marks"].(float64)
	fmt.Println(marks + 5)
}
```

```text
Meena 85 [art chess]
90
```

- JSON numbers become `float64` in an `any`.
- You need a type assertion to use them. See **Interfaces and Polymorphism**.
- A struct is safer when you know the shape.

**Simple lesson:** `json.Unmarshal(text, &value)` fills your value, and returns an error for bad JSON.

## Working with JSON

### JSON Struct Tags

Tags control the JSON key names and options.

Flip the options and watch the JSON change.

| Tag | Effect |
|---|---|
| `` `json:"name"` `` | use the key `name` |
| `` `json:"remark,omitempty"` `` | leave the field out when it is empty |
| `` `json:"-"` `` | never write or read this field |

```go runnable title="Tags in action"
package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name     string   `json:"name"`
	Class    string   `json:"class"`
	Marks    int      `json:"marks"`
	Clubs    []string `json:"clubs,omitempty"`
	Password string   `json:"-"`
}

func main() {
	s := Student{Name: "Asha", Class: "8A", Marks: 91, Password: "secret"}
	data, _ := json.Marshal(s)
	fmt.Println(string(data))
}
```

```text
{"name":"Asha","class":"8A","marks":91}
```

- `Clubs` is empty, so `omitempty` left it out.
- `Password` has `json:"-"`, so it is never written.

### Struct to JSON and JSON to Struct

```go runnable title="A round trip"
package main

import (
	"encoding/json"
	"fmt"
)

type Book struct {
	Title    string `json:"title"`
	Borrowed bool   `json:"borrowed"`
}

func main() {
	original := Book{"Gitanjali", true}
	data, _ := json.Marshal(original)
	fmt.Println(string(data))

	var copyBook Book
	json.Unmarshal(data, &copyBook)
	fmt.Println(copyBook == original)
}
```

```text
{"title":"Gitanjali","borrowed":true}
true
```

### Nested JSON

Nested structs make nested JSON.

```go runnable title="A class with a teacher and students"
package main

import (
	"encoding/json"
	"fmt"
)

type Teacher struct {
	Name    string `json:"name"`
	Subject string `json:"subject"`
}

type Class struct {
	Name     string   `json:"name"`
	Teacher  Teacher  `json:"teacher"`
	Students []string `json:"students"`
}

func main() {
	text := []byte(`{
		"name": "8A",
		"teacher": {"name": "Mrs. Iyer", "subject": "Maths"},
		"students": ["Asha", "Meena", "Kabir"]
	}`)
	var c Class
	if err := json.Unmarshal(text, &c); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(c.Teacher.Name, "teaches", c.Teacher.Subject)
	fmt.Println(len(c.Students), "students, first is", c.Students[0])
}
```

```text
Mrs. Iyer teaches Maths
3 students, first is Asha
```

- A JSON object inside an object maps to a struct field of struct type.
- A JSON array maps to a slice.

**Simple lesson:** tags name the keys, and nested structs and slices match nested JSON.

## Common Use Cases

### Configuration Files

```go runnable title="Read data/config.json"
package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type Config struct {
	School string `json:"school"`
	Year   int    `json:"year"`
	Debug  bool   `json:"debug"`
}

func main() {
	data, err := os.ReadFile("data/config.json")
	if err != nil {
		fmt.Println(err)
		return
	}
	var cfg Config
	if err := json.Unmarshal(data, &cfg); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(cfg.School, cfg.Year, cfg.Debug)
}
```

```text
Green Valley School 2025 false
```

### Local Data Storage

```go runnable title="Load, change and save a list"
package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type Student struct {
	Name  string `json:"name"`
	Class string `json:"class"`
	Marks int    `json:"marks"`
}

func main() {
	data, _ := os.ReadFile("data/students.json")
	var list []Student
	json.Unmarshal(data, &list)

	list = append(list, Student{"Meena", "8A", 85})
	out, _ := json.MarshalIndent(list, "", "  ")
	os.WriteFile("data/students.json", out, 0644)

	saved, _ := os.ReadFile("data/students.json")
	var check []Student
	json.Unmarshal(saved, &check)
	fmt.Println(len(check), check[2].Name)
}
```

```text
3 Meena
```

- Read the file, decode, change the data, encode, and write it back.
- This is how small apps save their data without a database. See **File Handling**.

### REST API Responses and Data Exchange

**API** = a way for programs to ask each other for data, often over the web.

**REST API response** = the JSON a web server sends back.

```go
// A tiny API on your computer: open http://localhost:8080/student
http.HandleFunc("/student", func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(Student{"Asha", "8A", 91})
})
```

- `json.NewEncoder(w).Encode(v)` writes JSON straight to the web response.
- `json.NewDecoder(r.Body).Decode(&v)` reads JSON straight from a request.
- Web servers cannot run in this browser lab, so this is shown as a plain block.

JSON numbers decoded into `any` are always `float64`, even `91`.

Decode into a struct with an `int` field when you need whole numbers.

**Easy way to remember**

- Go → JSON: `json.Marshal(v)` or `json.MarshalIndent(v, "", "  ")`
- JSON → Go: `json.Unmarshal(data, &v)` (always `&`)
- Key names: `` `json:"name"` ``. Skip empty: `omitempty`. Never: `-`.

**Simple lesson:** JSON is used for settings, saved data and talking to other programs.

## TL;DR

1. **JSON** = text data: objects `{ }`, arrays `[ ]`, strings, numbers, `true`/`false`, `null`.
2. **Encode** = `json.Marshal(v)` → `[]byte`. Pretty: `json.MarshalIndent(v, "", "  ")`.
3. **Decode** = `json.Unmarshal(data, &v)`. Pass a pointer. Check the error.
4. **Only exported fields** take part.
5. **Tags** = `` `json:"name"` ``, `omitempty`, `"-"`.
6. **Nested JSON** = nested structs and slices.
7. **Unknown shape** = `map[string]any`. Numbers become `float64`.
8. **Uses** = config files, API responses, local storage, data exchange.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Lowercase field names | All fields are encoded | Only exported fields | The field is missing from JSON. |
| `json.Unmarshal(data, s)` | A value works | Pass `&s` | `json: Unmarshal(non-pointer main.Student)` |
| Ignoring the Unmarshal error | JSON is always valid | Check `err` | A half-empty struct used silently. |
| `data["marks"].(int)` from `map[string]any` | JSON numbers are ints | They are `float64` | A type assertion panic. |
| Tag `json:name` without quotes | Quotes are optional | Write `` `json:"name"` `` | The tag is ignored. |
| A trailing comma in JSON | Like Go literals | JSON forbids it | `invalid character` error. |
| Printing `data` without `string()` | It is text | It is `[]byte` | You see a list of numbers. |

## Check Yourself

## Exercises

## What to Remember

- JSON is text data that every language understands.
- `json.Marshal` / `MarshalIndent` turn Go values into JSON.
- `json.Unmarshal(data, &v)` fills your value. Always pass a pointer, always check the error.
- Only exported fields are used. Tags set the names and options.
- Nested structs and slices match nested JSON.

Next: **Goroutines**. You will learn how Go runs many jobs at the same time.
