json-iterator/go

repository·master·Indexed 11 days ago

https://github.com/json-iterator/go

A high-performance, 100% compatible drop-in replacement for the standard Go encoding/json library. It is designed to improve JSON encoding and decoding speed and reduce memory allocations, offering features like the Any type for extracting values from nested structures without full struct models and streaming interfaces via NewDecoder and NewEncoder.

Tokens
19.6K
Snippets
82
Records
96
Agent score
90%

What's inside json-iterator/go

  1. Compare json-iterator performance with encoding/json

    master

    Json-iterator is designed for high performance. In typical benchmarks, it outperforms the standard library in both decoding and encoding speed and memory allocation.

    Note: Always benchmark with your own specific workload, as performance results depend heavily on the data input.

    |                 | ns/op       | allocation bytes | allocation times |
    | --------------- | ----------- | ---------------- | ---------------- |
    | std decode      | 35510 ns/op | 1960 B/op        | 99 allocs/op    |
    | jsoniter decode | 5623 ns/op  | 160 B/op         | 3 allocs/op     |
    | std encode      | 2213 ns/op  | 712 B/op         | 5 allocs/op     |
    | jsoniter encode | 837 ns/op   | 384 B/op         | 4 allocs/op     |
  2. Use json-iterator as a drop-in replacement for encoding/json

    master

    To achieve 100% compatibility with the standard library while benefiting from higher performance, use jsoniter.ConfigCompatibleWithStandardLibrary. This allows you to replace standard encoding/json calls with minimal changes to your code structure.

    import jsoniter "github.com/json-iterator/go"
    
    // Use this variable to access standard-library compatible methods
    var json = jsoniter.ConfigCompatibleWithStandardLibrary
    
    // For Marshalling:
    json.Marshal(&data)
    
    // For Unmarshalling:
    json.Unmarshal(input, &data)
  3. Handle invalid values using the Any type

    master

    When navigating JSON structures using the Any type, if a requested path does not exist or an error occurs, json-iterator returns an invalidAny object. This object represents an invalid state and provides several ways to handle it:

    1. Check for errors: Use LastError() to retrieve the error that caused the invalid state.
    2. Check the value type: Use ValueType() which will return InvalidValue for an invalid object.
    3. Safe conversion: Calling conversion methods like ToBool(), ToInt(), ToString(), etc., on an invalid object will return the zero value for that type (e.g., false, 0, or "") instead of panicking.
    4. Strict enforcement: If you want to ensure the value is valid and trigger a failure if it is not, use MustBeValid(), which will panic if the object is invalid.
    // Example of handling an invalid Any value
    val := any.Get("non_existent_key")
    
    if val.ValueType() == jsoniter.InvalidValue {
        fmt.Println("Error encountered:", val.LastError())
    }
    
    // Safe conversions return zero values
    str := val.ToString() // returns ""
    num := val.ToInt()    // returns 0
    
    // Or panic if invalid
    // val.MustBeValid()
  4. Check if an array is empty via type conversion

    master

    When calling type conversion methods like ToBool(), ToInt(), or ToFloat64() on an Any that represents an array, the behavior is based on whether the array is empty:

    • ToBool() returns true if the array has elements, false otherwise.
    • ToInt(), ToUint(), ToFloat64(), etc., return 1 (or 1.0) if the array is non-empty, and 0 if it is empty.
  5. Handle json.RawMessage and jsoniter.RawMessage

    master

    The library provides internal codecs to support both standard library and native json-iterator raw message types:

    • json.RawMessage: The standard library type. When decoding, it captures the raw bytes of the JSON value. When encoding, it writes the bytes directly to the stream.
    • jsoniter.RawMessage: The native json-iterator type. It behaves identically to json.RawMessage, allowing for seamless integration with json-iterator's high-performance stream processing.
  6. How Optional encoding and decoding works

    master

    In json-iterator, optional encoding and decoding refers to the handling of pointer types in JSON.

    • Encoding: When an OptionalEncoder encounters a nil pointer, it writes a JSON null to the stream. If the pointer is non-nil, it encodes the value the pointer points to.
    • Decoding: When an OptionalDecoder encounters a JSON null, it sets the target pointer to nil. If the JSON contains a value, the decoder allocates new memory for the type (if the pointer is currently nil) or reuses the existing instance (if the pointer is already allocated) before decoding the value into it.

    This mechanism ensures that Go pointers are correctly mapped to JSON null and vice-versa, supporting both the creation of new objects and the reuse of existing ones during decoding.

  7. Behavior of the Any type when encountering Nil values

    master

    When the jsoniter.Any type represents a JSON null value (internally handled by the nilAny implementation), calling type-specific conversion methods will return the Go zero value for that type instead of an error. This allows for safe, error-free access to values without explicit nil checks in many common scenarios.

    Conversion behaviors for Nil values:

    • ToBool() returns false
    • ToInt(), ToInt32(), ToInt64() return 0
    • ToUint(), ToUint32(), ToUint64() return 0
    • ToFloat32(), ToFloat64() return 0
    • ToString() returns "" (empty string)
    • GetInterface() returns nil
    • LastError() returns nil
    • ValueType() returns NilValue
    • MustBeValid() returns the Any instance itself
  8. Use RawMessage to delay JSON decoding

    master

    The RawMessage type allows you to capture a portion of a JSON document as a raw byte slice without immediately parsing it into a structured Go type. This is useful for delaying decoding until later in your application logic or for handling polymorphic JSON structures where the schema depends on a specific field.

    json-iterator provides specialized support for both its own RawMessage type and the standard library's json.RawMessage, ensuring they are handled efficiently during encoding and decoding processes.

  9. Use the Any interface for dynamic JSON manipulation

    master

    The Any interface provides a generic, lazy representation of JSON values. It is more powerful than json.RawMessage because it allows for easy type conversion, path-based navigation, and inspection of JSON structures without full unmarshaling into a concrete Go struct.

    Key capabilities include:

    • Type Conversion: Convert the JSON value to Go primitives using methods like ToInt(), ToString(), ToBool(), ToFloat64(), etc.
    • Path Navigation: Use Get(path ...interface{}) to traverse nested objects (using string keys) or arrays (using int indices).
    • Inspection: Check the ValueType(), get the number of elements via Size(), or retrieve object keys via Keys().
    • Lazy Parsing: Implementations like objectLazyAny and arrayLazyAny hold the raw bytes and only parse them when a value is actually requested, making it highly efficient for large JSON payloads where you only need specific fields.
    // Example of using Any for dynamic access
    // Assuming 'anyVal' is an Any object obtained via iter.ReadAny() or jsoniter.Unmarshal
    
    // 1. Navigate to a nested field: {"user": {"id": 123}}
    userID := anyVal.Get("user", "id").ToInt()
    
    // 2. Access array elements: {"tags": ["go", "json"]}
    tag := anyVal.Get("tags", 0).ToString()
    
    // 3. Check size and keys
    size := anyVal.Size()
    keys := anyVal.Keys()
  10. Customize JSON behavior using Config and API

    master

    In json-iterator, you do not interact with a global configuration directly. Instead, you define a Config struct to specify your desired JSON behavior and then call .Froze() to create an API instance. This API instance is a thread-safe, immutable object that provides all the primary Marshal and Unmarshal methods.

    Workflow

    1. Define a Config with your desired settings.
    2. Call cfg.Froze() to obtain an API instance.
    3. Use the API instance for all JSON operations to ensure consistent behavior across your application.
    import "github.com/json-iterator/go"
    
    // 1. Define configuration
    cfg := jsoniter.Config{
        EscapeHTML: false,
        UseNumber:  true,
    }
    
    // 2. Freeze to create an API instance
    api := cfg.Froze()
    
    // 3. Use the API
    data := []byte(`{"name": "json-iterator"}`)
    var m map[string]interface{}
    err := api.Unmarshal(data, &m)
  11. Use EncoderExtension and DecoderExtension maps

    master

    If you only need to provide a mapping of types to encoders or decoders without implementing the full Extension interface, you can use the EncoderExtension or DecoderExtension types. These are essentially maps that satisfy the Extension interface requirements by providing no-op implementations for most methods, while implementing CreateEncoder or CreateDecoder via map lookups.

    // Example of a DecoderExtension
    myDecoders := jsoniter.DecoderExtension{
        someType: &MyCustomDecoder{},
    }
    // This can be used as an extension if registered or passed to a context