Deferred JSON Parsing with json.RawMessage in Go

Use json.RawMessage to defer JSON parsing in Go. Handle polymorphic payloads, conditional decoding, and performance optimization for large documents.

Edge Cases

Detailed Explanation

Deferred Parsing with json.RawMessage

json.RawMessage is a raw encoded JSON value. It implements json.Marshaler and json.Unmarshaler, allowing you to delay parsing part of a JSON document.

The Problem

{
  "type": "user.created",
  "payload": { "id": 1, "name": "Alice" }
}
{
  "type": "order.placed",
  "payload": { "order_id": 99, "total": 49.99 }
}

The payload shape depends on type. You cannot define a single struct for it.

Solution: Two-Phase Parsing

type Event struct {
    Type    string          `json:"type"`
    Payload json.RawMessage `json:"payload"`
}

Phase 1 — Parse the envelope:

var event Event
json.Unmarshal(data, &event)

Phase 2 — Parse the payload based on type:

switch event.Type {
case "user.created":
    var user UserPayload
    json.Unmarshal(event.Payload, &user)
case "order.placed":
    var order OrderPayload
    json.Unmarshal(event.Payload, &order)
}

Performance Benefits

For large JSON documents where you only need a few fields, json.RawMessage avoids parsing unused sections entirely:

type LargeDoc struct {
    Header  Header          `json:"header"`
    Details json.RawMessage `json:"details"`
    Audit   json.RawMessage `json:"audit"`
}

You parse Header immediately and only unmarshal Details or Audit when needed.

Forwarding JSON Unchanged

json.RawMessage is also useful when you need to pass JSON through without modifying it:

type Proxy struct {
    Route   string          `json:"route"`
    Body    json.RawMessage `json:"body"`
}

The Body is stored as-is and can be re-serialized without any parsing or data loss.

Null Handling

A JSON null is valid json.RawMessage content. Check with:

if string(event.Payload) == "null" {
    // handle null payload
}

When to Use json.RawMessage

  • Polymorphic payloads (webhooks, event systems)
  • Partial parsing for performance
  • JSON passthrough / proxying
  • Schema-less storage (store raw JSON in a database)

Use Case

Event-driven architectures and webhook handlers must process messages with varying payload structures. json.RawMessage lets you inspect the event type first and then parse the payload into the correct Go type.

Try It — JSON to Go Struct Converter

Open full tool