json

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 3, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package json provides case-sensitive JSON unmarshaling functionality.

Unlike the standard encoding/json package which performs case-insensitive field matching, this package requires exact field name matches (respecting json tags).

This is particularly useful when:

  • You need strict validation of JSON field names
  • There's a risk of duplicate fields with different cases
  • You want to ensure only exact field matches are accepted
  • You're building APIs that require rigorous validation

Basic Usage:

import (
    stdjson "encoding/json"
    csjson "github.com/go-mg/casesensitive/json"
)

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

payload := `{"name":"john", "NAME":"doe", "email":"john@example.com"}`

// Standard encoding/json (case-insensitive)
var user1 User
stdjson.Unmarshal([]byte(payload), &user1)
// Result: user1.Name = "doe" (last value wins)

// casesensitive/json (case-sensitive)
var user2 User
csjson.Unmarshal([]byte(payload), &user2)
// Result: user2.Name = "john" (only exact "name" match)

Decoder with Strict Validation:

decoder := csjson.NewDecoder(strings.NewReader(payload))
decoder.DisallowUnknownFields()
err := decoder.Decode(&user)
// Returns error: json: unknown field "NAME"

JSON Tags Support:

The package respects json tags the same way as encoding/json:

type User struct {
    Name     string `json:"name"`          // Maps to "name"
    Email    string `json:"email"`         // Maps to "email"
    Password string `json:"-"`             // Ignored
    Age      int    `json:"age,omitempty"` // Maps to "age"
    Nickname string                        // No tag: maps to "Nickname" (exact)
}

Performance Considerations:

This package is slightly slower than encoding/json due to additional validation. Use encoding/json when performance is critical and case-sensitivity is not required.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Unmarshal

func Unmarshal(data []byte, v any) error

Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. Unlike standard json.Unmarshal, this function is case-sensitive at all nesting levels and will only match exact field names (respecting json tags). v can be a pointer to a struct or a pointer to a slice of structs.

Example
package main

import (
	"fmt"

	"github.com/go-mg/casesensitive/json"
)

func main() {
	type User struct {
		Name  string `json:"name"`
		Email string `json:"email"`
	}

	payload := `{"name":"john", "NAME":"hacker", "email":"john@example.com"}`

	var user User
	if err := json.Unmarshal([]byte(payload), &user); err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Name: %s\n", user.Name)
	fmt.Printf("Email: %s\n", user.Email)
}
Output:
Name: john
Email: john@example.com
Example (Array)
package main

import (
	"fmt"

	"github.com/go-mg/casesensitive/json"
)

func main() {
	type Item struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	}

	payload := `[{"id":1,"name":"first"},{"id":2,"name":"second"}]`

	var items []Item
	if err := json.Unmarshal([]byte(payload), &items); err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	for _, item := range items {
		fmt.Printf("ID: %d, Name: %s\n", item.ID, item.Name)
	}
}
Output:
ID: 1, Name: first
ID: 2, Name: second

Types

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder reads and decodes JSON values from an input stream with case-sensitive field matching.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/go-mg/casesensitive/json"
)

func main() {
	type User struct {
		Name  string `json:"name"`
		Email string `json:"email"`
	}

	payload := `{"name":"john","email":"john@example.com"}`

	var user User
	decoder := json.NewDecoder(strings.NewReader(payload))
	err := decoder.Decode(&user)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Name: %s\n", user.Name)
	fmt.Printf("Email: %s\n", user.Email)
}
Output:
Name: john
Email: john@example.com
Example (DisallowUnknownFields)
package main

import (
	"fmt"
	"strings"

	"github.com/go-mg/casesensitive/json"
)

func main() {
	type User struct {
		Name  string `json:"name"`
		Email string `json:"email"`
	}

	payload := `{"name":"john","NAME":"hacker","email":"john@example.com"}`

	var user User
	decoder := json.NewDecoder(strings.NewReader(payload))
	decoder.DisallowUnknownFields()
	err := decoder.Decode(&user)

	fmt.Printf("Error: %v\n", err)
}
Output:
Error: json: unknown field "NAME"
Example (TrailingDataRejected)
package main

import (
	"fmt"
	"strings"

	"github.com/go-mg/casesensitive/json"
)

func main() {
	type User struct {
		Name string `json:"name"`
	}

	payload := `{"name":"john"}extra_data`

	var user User
	decoder := json.NewDecoder(strings.NewReader(payload))
	err := decoder.Decode(&user)

	fmt.Printf("Error: %v\n", err)
}
Output:
Error: json: invalid character after top-level value

func NewDecoder

func NewDecoder(r io.Reader) *Decoder

NewDecoder returns a new decoder that reads from r.

func (*Decoder) AllowTrailingData

func (d *Decoder) AllowTrailingData() *Decoder

AllowTrailingData allows the Decoder to accept input with data after the JSON value. By default, the decoder rejects trailing data to prevent injection attacks. Use this only when you need to parse multiple JSON values from a stream.

func (*Decoder) Decode

func (d *Decoder) Decode(v any) error

Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v. Unlike standard json.Decoder, this decoder is case-sensitive and validates that the entire input is valid JSON without trailing data (unless AllowTrailingData is called). v can be a pointer to a struct or a pointer to a slice of structs.

func (*Decoder) DisallowUnknownFields

func (d *Decoder) DisallowUnknownFields() *Decoder

DisallowUnknownFields causes the Decoder to return an error when the destination is a struct and the input contains object keys which do not match any non-ignored, exported fields in the destination (case-sensitive).

func (*Decoder) More

func (d *Decoder) More() bool

More returns true if there is another element in the current array or object being parsed.

func (*Decoder) Token

func (d *Decoder) Token() (json.Token, error)

Token returns the next JSON token in the input stream.

func (*Decoder) UseNumber

func (d *Decoder) UseNumber() *Decoder

UseNumber causes the Decoder to unmarshal a number into an interface{} as a Number instead of as a float64.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL