Go JSON Parsing: encoding/json vs jsontext, and Why You Might Want Both

Go JSON Parsing: encoding/json vs jsontext, and Why You Might Want Both
Photo by Queslei Jonas Dos Santos Oliveira / Unsplash

When working with JSON in Go, most developers naturally reach for the standard library's encoding/json package. It’s convenient, reliable, and has been the default for years. But if you’ve ever found yourself needing more control, better performance for large payloads, or streaming JSON processing, you might want to look into jsontext.

This article will help you understand the difference between encoding/json and golang.org/x/text/json/jsontext, why you might use both, and how they can complement each other.


🧰 What Is encoding/json?

The encoding/json package is the standard Go library for working with JSON. It provides high-level APIs to marshal and unmarshal Go structs.

import "encoding/json"

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

jsonStr := `{"name": "Sony", "age": 30}`
var u User
_ = json.Unmarshal([]byte(jsonStr), &u)

✅ Pros

  • Easy to use.
  • Supports struct tags and reflection.
  • Ideal for APIs and typical request/response flows.

❌ Cons

  • Doesn’t work well with huge or streaming JSON.
  • Not suitable for partial or dynamic parsing.
  • Less efficient for token-level manipulation.

🔬 Enter jsontext from golang.org/x/text/json/jsontext

jsontext is a low-level JSON tokenizer and parser. It’s not part of the standard library, but maintained by the Go team in the golang.org/x/text modules.

You can install it using:

go get golang.org/x/text/json/jsontext

Why Use It?

Instead of decoding whole structs, you can read JSON token-by-token (objects, arrays, strings, numbers) — which is ideal for large files, logs, or streams.

dec := jsontext.NewDecoder(strings.NewReader(`{"name": "Sony", "age": 30}`))
for {
	tok, err := dec.ReadToken()
	if err != nil {
		break
	}
	fmt.Printf("Token: %#v\n", tok)
}

✅ Pros

  • Works well for huge JSON files or streamed data.
  • Gives fine-grained control.
  • Allows partial parsing — skip what you don’t need.

❌ Cons

  • Requires more manual work.
  • Doesn’t map JSON to structs automatically.
  • Not for casual or simple use cases.

🧠 When Should You Use Each?

Scenario Use encoding/json Use jsontext
Parsing API responses ✅ Easy and clean ❌ Overkill
Reading large logs ❌ Memory-heavy ✅ Efficient
Selective JSON field parsing ❌ Not possible ✅ Perfect
Streaming JSON from stdin or socket ❌ Complex ✅ Designed for it
Token inspection or filtering ❌ Limited ✅ Excellent

🤝 Combine the Best of Both Worlds

You don’t have to choose only one. You can use jsontext to navigate or isolate part of a JSON payload, and then use encoding/json to map that part to a struct.

Here’s a real-world example: imagine this JSON input:

{
  "meta": { "version": "1.0", "timestamp": "2025-04-13T12:00:00Z" },
  "data": { "name": "Sony", "age": 30 }
}

We want to ignore the "meta" section, and decode only "data".

🧪 Code Example

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
	"golang.org/x/text/json/jsontext"
)

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

func main() {
	input := `{
		"meta": { "version": "1.0", "timestamp": "2025-04-13T12:00:00Z" },
		"data": { "name": "Sony", "age": 30 }
	}`

	dec := jsontext.NewDecoder(strings.NewReader(input))

	var foundDataRaw json.RawMessage
	var currentKey string

	for {
		tok, err := dec.ReadToken()
		if err != nil {
			break
		}

		if key, ok := tok.(string); ok {
			currentKey = key
			continue
		}

		if currentKey == "data" {
			var buf bytes.Buffer
			_ = dec.ReadValue(&buf)
			foundDataRaw = buf.Bytes()
			break
		}
	}

	var d Data
	_ = json.Unmarshal(foundDataRaw, &d)

	fmt.Printf("Name: %s, Age: %d\n", d.Name, d.Age)
}

✅ Output:

Name: Sony, Age: 30

This lets you skip irrelevant fields and only decode what matters. Perfect for performance-critical or structured log processing.


⚠️ Things to Keep in Mind

  • jsontext is part of golang.org/x, not the standard library — it may evolve independently.
  • It’s not a replacement for encoding/json, but a power tool when you need more than what encoding/json offers.
  • For large files or streamed inputs (e.g., WebSocket JSON messages), jsontext is a solid choice.

🧾 Finally

Use encoding/json for convenience, and use jsontext when you need control. And if you’re building robust services that consume third-party JSON data — or handling massive logs — you’ll love combining both.

If you want to process JSON intelligently, without loading the entire blob into memory, or just need a laser-focused way to extract fields, jsontext will make your tooling smarter and faster.

Support Us