# encoding/json vs encoding/json/v2 in Go 1.27: Key Differences Explained

> Explore Go 1.27's encoding/json vs encoding/json/v2. Discover the stricter JSON handling in v2, enforcing spec compliance for invalid UTF-8, duplicate keys, and nil defaults. Upgrade your Go JSON parsing.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: deep-dive
- Published: 2026-08-30

---

**Go 1.27 introduces `encoding/json/v2`, a stricter alternative to the standard `encoding/json` package that rejects invalid UTF-8 and duplicate keys while changing nil serialization defaults to enforce JSON spec compliance.**

The Go standard library's approach to JSON handling underwent a significant evolution with the Go 1.27 release. According to the JetBrains/go-modern-guidelines repository, the new `encoding/json/v2` module provides the same public API as its predecessor but enforces stricter conformance to the JSON specification, fundamentally altering how edge cases like invalid UTF-8 sequences and duplicate object keys are processed.

## Critical Behavioral Differences Between encoding/json Versions

The v2 package maintains API compatibility with v1 but implements breaking behavioral changes in four critical areas.

### Strict UTF-8 Validation

In the legacy `encoding/json`, invalid UTF-8 sequences are silently accepted and replaced during marshaling. The `encoding/json/v2` implementation rejects invalid UTF-8 content entirely, returning a validation error rather than producing non-compliant JSON output.

### Duplicate Key Rejection

While the v1 package keeps the last occurrence of duplicate object keys without warning, `encoding/json/v2` treats duplicate keys as an error condition. This prevents silent data loss when unmarshaling objects with repeated fields.

### Nil Serialization Changes

The v1 package serializes nil slices and maps as `null` values. Conversely, `encoding/json/v2` marshals nil slices as empty arrays `[]` and nil maps as empty objects `{}`, eliminating ambiguity between absent and empty collections.

### Enhanced Type Validation

`encoding/json` permits many Go types that lack clean JSON representations. The v2 package reports explicit errors for types that cannot sensibly serialize to JSON, catching encoding issues at the API boundary rather than during transmission.

## Practical Code Comparison

The following examples demonstrate how identical structs produce different JSON output between versions.

### Legacy encoding/json Behavior

```go
import "encoding/json"

type User struct {
    Name string
    Tags []string // nil slice encodes as null
}

func main() {
    u := User{Name: "Alice"}
    // Marshals Tags as null
    data, _ := json.Marshal(u)
    fmt.Println(string(data)) // {"Name":"Alice","Tags":null}
}

```

### New encoding/json/v2 Behavior

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

type User struct {
    Name string
    Tags []string // nil slice encodes as empty array
}

func main() {
    u := User{Name: "Alice"}
    // Marshals Tags as []
    data, err := json.Marshal(u)
    if err != nil {
        log.Fatalf("marshal error: %v", err)
    }
    fmt.Println(string(data)) // {"Name":"Alice","Tags":[]}
}

```

## Migration Strategy and Compatibility

The import path changes from `import "encoding/json"` to `import "encoding/json/v2"` (commonly aliased as `json`). The guidelines in [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) explicitly recommend adopting v2 for **new code only**, as simple import changes alter wire formats for nil values and may cause previously working systems to error on invalid UTF-8 or duplicates.

For gradual migration, the v2 package provides compatibility helpers such as `jsonv1.DefaultOptionsV1()` to retain prior behavior during transition periods. Existing codebases should only migrate after deliberate planning, as the stricter defaults may break API contracts with consumers expecting `null` for empty collections.

## Source Files and Implementation Details

The JetBrains/go-modern-guidelines repository documents these differences across three key locations:

- **[`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json)** — Contains the core recommendation to prefer `encoding/json/v2` for new projects while cautioning against hasty migration of existing code.
- **[`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md)** — Lists practical implications of the behavioral changes, including wire format differences and error handling modifications.
- **[`CHANGELOG.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/CHANGELOG.md)** — Documents the addition of v2 guidance and tracks version-specific implementation details.

## Summary

- `encoding/json/v2` rejects invalid UTF-8 sequences rather than silently replacing them with replacement characters.
- Duplicate object keys generate errors instead of silently overwriting earlier values.
- Nil slices and maps serialize as empty collections (`[]` and `{}`) rather than `null`.
- Stricter type validation prevents marshaling of Go types that lack sensible JSON representations.
- New projects should default to v2 immediately, while existing code requires planned migration using compatibility helpers.
- The public API remains unchanged, but behavioral defaults differ significantly enough to affect API consumers.

## Frequently Asked Questions

### Can I use encoding/json/v2 in Go versions earlier than 1.27?

No, `encoding/json/v2` was introduced specifically in Go 1.27. Earlier Go versions must continue using the legacy `encoding/json` package or third-party alternatives, as the v2 module relies on runtime features and standard library changes available only in 1.27+.

### Will existing code break if I change the import to encoding/json/v2?

Yes, changing the import path without additional configuration will alter serialization behavior. The v2 package uses stricter defaults that may cause previously working code to return errors for invalid UTF-8 or duplicate keys, and nil values will serialize differently, potentially breaking API contracts.

### How do I maintain v1 behavior while using the v2 package?

The `encoding/json/v2` module provides compatibility helpers such as `jsonv1.DefaultOptionsV1()` that configure the v2 encoder to mimic legacy behavior. However, the repository guidelines recommend using these only during explicit migration periods rather than as a permanent solution for new development.

### Should I migrate existing production code to encoding/json/v2 immediately?

According to [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json), you should adopt `encoding/json/v2` for new code only. Existing codebases should migrate only after deliberate planning and comprehensive testing, as the wire format changes for nil values and stricter validation may affect downstream API consumers.