# encoding/json/v2 vs encoding/json in Go 1.27+: Impact and Migration Guide

> Explore the impact of encoding/json/v2 vs encoding/json in Go 1.27+. Understand strict validation changes and wire-format incompatibilities. Get a migration guide now.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: migration-guide
- Published: 2026-09-04

---

**TLDR:** The `encoding/json/v2` module introduced in Go 1.27+ enforces strict validation that rejects invalid UTF-8 strings, duplicate object keys, and type mismatches, while changing nil slice encoding from `null` to empty arrays `[]`, creating high-impact wire-format incompatibilities with the legacy `encoding/json` package.

The JetBrains/go-modern-guidelines repository classifies the adoption of `encoding/json/v2` as a **High** impact change for Go 1.27+ projects, noting that an import change can alter wire behavior and break existing API consumers. Understanding the precise behavioral differences between `encoding/json/v2` vs `encoding/json` is critical for maintaining system compatibility during migration.

## Key Behavioral Differences

According to [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) (lines 46-53), the `encoding/json/v2` package introduces several breaking changes compared to v1:

- **Invalid UTF-8 rejection**: While `encoding/json` silently allows and replaces invalid UTF-8 sequences, `encoding/json/v2` **rejects** invalid UTF-8 and returns an explicit error.
- **Duplicate key detection**: `encoding/json` keeps the last occurrence of duplicate object fields and drops earlier ones silently; `encoding/json/v2` **fails** with an error when encountering duplicate keys.
- **Nil slice encoding**: `encoding/json` encodes nil slices and maps as `null`; `encoding/json/v2` encodes them as empty arrays `[]` or objects `{}` by default.
- **Type mismatch reporting**: `encoding/json` may succeed silently or produce unexpected output when unmarshaling into invalid Go types; `encoding/json/v2` **detects** and reports mismatched types immediately.
- **Wire format compatibility**: Changing imports without adjustment alters the serialized output, potentially breaking clients expecting the legacy format.

## Migration Strategy

The repository recommends distinct approaches based on project maturity, as documented in the guideline source files.

### New Projects

For new Go 1.27+ codebases, import `encoding/json/v2` directly to benefit from stricter defaults and improved error detection:

```go
import "encoding/json/v2"

type Pet struct {
    Name      string
    Nicknames []string
}

// Encodes nil Nicknames as [] instead of null
body, err := json.Marshal(Pet{Name: "Remi"})
// Output: {"Name":"Remi","Nicknames":[]}

```

### Existing Codebases

For legacy systems, the guidelines in [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) (lines 94-107) recommend preserving compatibility using `jsonv1.DefaultOptionsV1()`:

```go
import (
    jsonv1 "encoding/json"
    json   "encoding/json/v2"
)

type Pet struct {
    Name      string
    Nicknames []string
}

// Preserves legacy wire format (null for nil slices)
body, err := json.Marshal(
    Pet{Name: "Remi"},
    jsonv1.DefaultOptionsV1(),
)
// Output: {"Name":"Remi","Nicknames":null}

```

## Progressive Adoption Path

After validating consumer compatibility, you can progressively adopt v2 behaviors. As shown in lines 124-146 of the guidelines JSON, combine compatibility options with specific v2 overrides:

```go
import (
    jsonv1 "encoding/json"
    json   "encoding/json/v2"
)

type Pet struct {
    Name      string
    Nicknames []string
}

// Maintain v1 compatibility except for nil slice encoding
body, err := json.Marshal(
    Pet{Name: "Remi"},
    jsonv1.DefaultOptionsV1(),
    json.FormatNilSliceAsNull(false), // Switch to v2 behavior: [] instead of null
)

```

## Source Code Reference

The implementation details and migration patterns are defined across the following files in the JetBrains/go-modern-guidelines repository:

- **[`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json)**: Contains the canonical guideline definition, including impact ratings (`"impact": "High"`), behavioral specifications, and code examples at lines 46-53, 94-107, and 124-146.
- **[`internal/guidelines/guidelines.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.go)**: Loads the embedded JSON guidelines and exposes them to CLI tooling.
- **[`internal/guidelines/guidelines_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines_test.go)**: Validates guideline data integrity and example correctness.
- **[`README.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/README.md)** and **documentation**: High-level overviews referencing the `encoding/json/v2` recommendations.

## Summary

- **`encoding/json/v2`** rejects invalid UTF-8, duplicate keys, and type mismatches that v1 silently accepts.
- **Nil slices** encode as `[]` in v2 versus `null` in v1, altering wire format compatibility.
- The JetBrains/go-modern-guidelines repository rates this migration as **High** impact due to potential client-breaking changes.
- Use **`jsonv1.DefaultOptionsV1()`** (imported as an alias) to preserve legacy behavior while adopting the v2 package.
- Reference **[`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json)** for authoritative configuration details and line-specific examples.

## Frequently Asked Questions

### What is the difference between `encoding/json` and `encoding/json/v2` in Go?

`encoding/json/v2` is a new module introduced in Go 1.27+ that provides stricter JSON validation and altered serialization defaults compared to the v1 package. While v1 allows invalid UTF-8 and silently handles duplicate keys, v2 rejects these conditions with errors and changes nil slice encoding from `null` to empty arrays.

### Why does `encoding/json/v2` change nil slice encoding?

The v2 module encodes nil slices as empty arrays `[]` (and nil maps as `{}`) by default to provide more consistent round-trip behavior and avoid the ambiguity of `null` values in JSON APIs. This change improves type safety but breaks compatibility with systems expecting the legacy `null` representation.

### How can I migrate to `encoding/json/v2` without breaking existing clients?

Import both packages using aliases, then pass `jsonv1.DefaultOptionsV1()` to your v2 `Marshal` calls to preserve the original wire format. After testing consumer compatibility, gradually remove compatibility options or apply specific overrides like `json.FormatNilSliceAsNull(false)` to adopt modern defaults.

### Where are the official guidelines for `encoding/json/v2` impact documented?

The authoritative source is [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) in the JetBrains/go-modern-guidelines repository, specifically lines 46-53 for impact assessment and lines 94-146 for migration examples. This file is loaded by [`internal/guidelines/guidelines.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.go) and validated by [`internal/guidelines/guidelines_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines_test.go).