Documentation
¶
Overview ¶
Package jsonrpc provides a Go implementation of the JSON-RPC 2.0 specification, as well as tools to parse and work with JSON-RPC requests and responses.
Index ¶
- Constants
- func EncodeBatchRequest(reqs []*Request) ([]byte, error)
- func EncodeBatchResponse(resps []*Response) ([]byte, error)
- func RandomJSONRPCID() int64
- func SetPerformanceProfile(profile PerformanceProfile)
- type DecodeOption
- type Error
- type PerformanceProfile
- type Request
- func DecodeBatchRequest(data []byte, opts ...DecodeOption) ([]*Request, error)
- func DecodeBatchRequestFromReader(r io.Reader, expectedSize int, opts ...DecodeOption) ([]*Request, error)
- func DecodeRequest(data []byte, opts ...DecodeOption) (*Request, error)
- func DecodeRequestOrBatch(data []byte, opts ...DecodeOption) (reqs []*Request, isBatch bool, err error)
- func NewBatchNotification(methods []string, params []any) ([]*Request, error)
- func NewBatchRequest(methods []string, params []any) ([]*Request, error)
- func NewNotification(method string, params any) *Request
- func NewRequest(method string, params any) *Request
- func NewRequestWithID(method string, params any, id any) *Request
- func (r *Request) IDString() string
- func (r *Request) IsEmpty() bool
- func (r *Request) IsNotification() bool
- func (r *Request) MarshalJSON() ([]byte, error)
- func (r *Request) String() string
- func (r *Request) UnmarshalJSON(data []byte) error
- func (r *Request) UnmarshalParams(dst any) error
- func (r *Request) Validate() error
- type Response
- func DecodeBatchResponse(data []byte, opts ...DecodeOption) ([]*Response, error)
- func DecodeBatchResponseFromReader(r io.Reader, expectedSize int, opts ...DecodeOption) ([]*Response, error)
- func DecodeResponse(data []byte, opts ...DecodeOption) (*Response, error)
- func DecodeResponseFromReader(r io.Reader, expectedSize int, opts ...DecodeOption) (*Response, error)
- func DecodeResponseOrBatch(data []byte, opts ...DecodeOption) (resps []*Response, isBatch bool, err error)
- func NewErrorResponse(id any, err *Error) *Response
- func NewResponse(id any, result any) (*Response, error)
- func NewResponseFromRaw(id any, rawResult json.RawMessage) (*Response, error)
- func (r *Response) Clone() (*Response, error)
- func (r *Response) Equals(other *Response) bool
- func (r *Response) Err() *Error
- func (r *Response) Free()
- func (r *Response) IDOrNil() any
- func (r *Response) IDString() string
- func (r *Response) IsEmpty() bool
- func (r *Response) MarshalJSON() ([]byte, error)
- func (r *Response) PeekBytesByPath(path ...any) ([]byte, error)
- func (r *Response) PeekStringByPath(path ...any) (string, error)
- func (r *Response) RawResult() json.RawMessage
- func (r *Response) Size() int
- func (r *Response) String() string
- func (r *Response) Unmarshal(dst any) error
- func (r *Response) UnmarshalError() error
- func (r *Response) UnmarshalJSON(data []byte) error
- func (r *Response) UnmarshalResult(dst any) error
- func (r *Response) Validate() error
- func (r *Response) Version() string
- func (r *Response) WithID(newID any) (*Response, error)
- func (r *Response) WriteTo(w io.Writer) (n int64, err error)
Examples ¶
Constants ¶
const ( InvalidRequest = -32600 MethodNotFound = -32601 InvalidParams = -32602 ServerSideException = -32603 ParseError = -32700 )
JSON-RPC error codes
Variables ¶
This section is empty.
Functions ¶
func EncodeBatchRequest ¶
EncodeBatchRequest marshals a slice of JSON-RPC requests into a batch. Returns an error if: - Input slice is empty - Any request fails validation
func EncodeBatchResponse ¶
EncodeBatchResponse marshals a slice of JSON-RPC responses into a batch. Returns an error if: - Input slice is empty - Any response fails validation
func RandomJSONRPCID ¶
func RandomJSONRPCID() int64
RandomJSONRPCID returns a randomly generated value appropriate for a JSON-RPC ID field. Returns an int64 in the range [0, 2147483647] (int32 range) for compatibility.
func SetPerformanceProfile ¶ added in v0.8.0
func SetPerformanceProfile(profile PerformanceProfile)
SetPerformanceProfile configures the JSON encoding/decoding behavior for all operations. This function is thread-safe and affects all subsequent JSON operations in the package.
The available profiles are:
- ProfileDefault: Recommended for most users (efficient + safe)
- ProfileCompatible: Use when migrating from encoding/json
- ProfileBalanced: Production apps wanting safe optimizations
- ProfileFast: Internal services with controlled data
- ProfileAggressive: Maximum speed, use with caution
Example usage:
// Use the balanced profile jsonrpc.SetPerformanceProfile(jsonrpc.ProfileBalanced)
Types ¶
type DecodeOption ¶ added in v0.9.0
type DecodeOption func(*decodeOptions)
DecodeOption configures decoding behavior. Pass options to decode entry points such as DecodeRequest, DecodeResponse, and their batch variants. Options only affect decoding; the encode path always emits strict JSON-RPC 2.0.
func WithAllowJSONRPC1 ¶ added in v0.9.0
func WithAllowJSONRPC1() DecodeOption
WithAllowJSONRPC1 relaxes version validation on decode to accept JSON-RPC 1.0 payloads, which carry no "jsonrpc" field (or an explicit "jsonrpc":"1.0") and may include both result and a null error. The decoded version string is preserved as-is. Without this option, decoding stays strict JSON-RPC 2.0.
type Error ¶
type Error struct {
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
Data any `json:"data,omitempty"` // Optional data field
}
Error represents a JSON-RPC error.
Example (Data) ¶
ExampleError_data demonstrates using the Data field in errors.
// Create an error with additional data
err := &Error{
Code: -32602,
Message: "Invalid params",
Data: map[string]any{
"field": "email",
"reason": "invalid format",
},
}
fmt.Printf("Code: %d\n", err.Code)
fmt.Printf("Message: %s\n", err.Message)
fmt.Printf("Data type: %T\n", err.Data)
Output: Code: -32602 Message: Invalid params Data type: map[string]interface {}
func (*Error) Equals ¶
Equals compares the contents of two JSON-RPC errors for equality. Returns true if both errors have the same Code and Message.
Note: The Data field is intentionally excluded from comparison because:
- Data has type `any`, making deep comparison complex and expensive
- Error equality is typically determined by code + message alone
- Data is optional and used for supplementary information
If you need to compare Data fields, do so separately after calling Equals().
func (*Error) IsEmpty ¶
IsEmpty returns true if the error is empty, which is if the error is nil or both code and message are empty.
Note: Zero error codes are valid per JSON-RPC 2.0 spec, but are treated as "empty" when both code=0 and message="". This helps identify placeholder or uninitialized errors.
func (*Error) UnmarshalJSON ¶
UnmarshalJSON unmarshals an error from a raw JSON-RPC response. The unmarshal logic uses several fallbacks to ensure an error is produced.
func (*Error) Validate ¶
Validate checks if the error is valid according to the JSON-RPC specification. An error is valid if it has at least one of: a non-zero code or a non-empty message. Zero error codes are allowed per JSON-RPC 2.0 spec, though they're treated as "empty" by IsEmpty() when combined with an empty message.
type PerformanceProfile ¶ added in v0.8.0
type PerformanceProfile int
PerformanceProfile defines a set of options for the sonic JSON parser, optimized for different usecases. Each profile represents a trade-off between performance, safety, and compatibility.
const ( // ProfileDefault uses sonic's default configuration, providing a balance of efficiency // and safety. Provides excellent performance without sacrificing robustness. ProfileDefault PerformanceProfile = iota // ProfileCompatible mimics encoding/json behavior for maximum compatibility. // Use this when migrating from encoding/json and you need identical output formatting. // // Note: This is slower than ProfileDefault due to sorting and escaping. ProfileCompatible // ProfileBalanced applies safe performance optimizations without compromising robustness. // This profile disables unnecessary features (HTML escaping, key sorting) while keeping // all safety validations enabled. ProfileBalanced // ProfileFast uses sonic's official ConfigFastest settings, prioritizing speed while // maintaining reasonable safety. Skips validation of already-validated JSON. // // Based on the sonic.ConfigFastest configuration. ProfileFast // ProfileAggressive maximizes performance by disabling most validation and safety features. // Only use this when you have complete control over input sources and have profiled to // confirm JSON encoding is a bottleneck. // // WARNING: Can panic or produce invalid JSON with malformed input. ProfileAggressive )
func GetPerformanceProfile ¶ added in v0.8.0
func GetPerformanceProfile() PerformanceProfile
GetPerformanceProfile returns the currently active performance profile.
type Request ¶
type Request struct {
JSONRPC string `json:"jsonrpc"`
ID any `json:"id,omitempty"`
Method string `json:"method"`
Params any `json:"params,omitempty"`
}
Request is a struct for a JSON-RPC request. It conforms to the JSON-RPC 2.0 specification except that the ID field is allowed to be fractional.
Example (Params_named) ¶
ExampleRequest_params_named demonstrates using named parameters (object).
// Create a request with named parameters
req := NewRequest("updateUser", map[string]any{
"userId": 123,
"name": "Alice",
"active": true,
})
fmt.Printf("Method: %s\n", req.Method)
fmt.Printf("Params type: %T\n", req.Params)
Output: Method: updateUser Params type: map[string]interface {}
Example (Params_nil) ¶
ExampleRequest_params_nil demonstrates a request without parameters.
// Create a request with no parameters
req := NewRequest("getServerTime", nil)
fmt.Printf("Method: %s\n", req.Method)
fmt.Printf("Params: %v\n", req.Params)
Output: Method: getServerTime Params: <nil>
Example (Params_positional) ¶
ExampleRequest_params_positional demonstrates using positional parameters (array).
// Create a request with positional parameters
req := NewRequest("subtract", []any{42, 23})
fmt.Printf("Method: %s\n", req.Method)
fmt.Printf("Params type: %T\n", req.Params)
fmt.Printf("Params: %v\n", req.Params)
Output: Method: subtract Params type: []interface {} Params: [42 23]
func DecodeBatchRequest ¶
func DecodeBatchRequest(data []byte, opts ...DecodeOption) ([]*Request, error)
DecodeBatchRequest parses a JSON-RPC batch request from a byte slice. Returns an error if: - Input is not a JSON array - Array is empty - Any element fails to parse as a valid Request
func DecodeBatchRequestFromReader ¶
func DecodeBatchRequestFromReader( r io.Reader, expectedSize int, opts ...DecodeOption, ) ([]*Request, error)
DecodeBatchRequestFromReader parses a JSON-RPC batch request from an io.Reader.
func DecodeRequest ¶
func DecodeRequest(data []byte, opts ...DecodeOption) (*Request, error)
DecodeRequest parses a JSON-RPC request from a byte slice. Pass WithAllowJSONRPC1 to accept JSON-RPC 1.0 requests; without it, decoding is strict JSON-RPC 2.0.
func DecodeRequestOrBatch ¶
func DecodeRequestOrBatch( data []byte, opts ...DecodeOption, ) (reqs []*Request, isBatch bool, err error)
DecodeRequestOrBatch attempts to parse either a single request or a batch of requests. - For single requests: returns slice with one element - For batch requests: returns slice with multiple elements
func NewBatchNotification ¶
NewBatchNotification creates a batch of notifications (requests without IDs).
func NewBatchRequest ¶
NewBatchRequest creates a batch of JSON-RPC requests from methods and params. Each request receives an auto-generated ID.
Example ¶
ExampleNewBatchRequest demonstrates creating batch requests with different param types.
methods := []string{"sum", "subtract", "getUser"}
params := []any{
[]any{1, 2, 3}, // positional params for sum
[]any{10, 5}, // positional params for subtract
map[string]any{"id": 123}, // named params for getUser
}
reqs, err := NewBatchRequest(methods, params)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
for i, req := range reqs {
fmt.Printf("Request %d: %s\n", i, req.Method)
}
Output: Request 0: sum Request 1: subtract Request 2: getUser
func NewNotification ¶
NewNotification creates a JSON-RPC 2.0 notification (request without ID).
func NewRequest ¶
NewRequest creates a JSON-RPC 2.0 request with an auto-generated ID.
func NewRequestWithID ¶
NewRequestWithID creates a JSON-RPC 2.0 request with a specific ID.
func (*Request) IsEmpty ¶
IsEmpty returns whether the Request is empty. A request is considered empty if the method field is empty.
func (*Request) IsNotification ¶
IsNotification returns true if this is a notification (no ID expected).
func (*Request) MarshalJSON ¶
MarshalJSON marshals the Request to a JSON byte slice.
func (*Request) String ¶
String returns a string representation of the JSON-RPC request. Note: implements the fmt.Stringer interface.
func (*Request) UnmarshalJSON ¶
UnmarshalJSON unmarshals a JSON-RPC request from a JSON byte slice. It enforces strict JSON-RPC 2.0; use DecodeRequest with WithAllowJSONRPC1 to accept 1.0 requests.
func (*Request) UnmarshalParams ¶
UnmarshalParams decodes the Params field into the provided destination pointer. This is a convenience method for unmarshaling structured parameters.
Example ¶
ExampleRequest_UnmarshalParams demonstrates unmarshaling params into a struct.
// Create a request with structured params
req := NewRequest("createUser", map[string]any{
"name": "Bob",
"email": "bob@example.com",
"age": 30,
})
// Define target struct
type UserParams struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
// Unmarshal params into struct
var params UserParams
if err := req.UnmarshalParams(¶ms); err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Printf("Name: %s, Email: %s, Age: %d\n", params.Name, params.Email, params.Age)
Output: Name: Bob, Email: bob@example.com, Age: 30
type Response ¶
type Response struct {
// contains filtered or unexported fields
}
Response is a struct for JSON-RPC responses conforming to the JSON-RPC 2.0 specification. Response instances are immutable after decoding and safe for concurrent reads.
The Response type uses lazy unmarshaling for the id and error fields to optimize performance.
func DecodeBatchResponse ¶
func DecodeBatchResponse(data []byte, opts ...DecodeOption) ([]*Response, error)
DecodeBatchResponse parses a JSON-RPC batch response from a byte slice. Returns an error if: - Input is not a JSON array - Array is empty - Any element fails to parse as a valid Response
func DecodeBatchResponseFromReader ¶
func DecodeBatchResponseFromReader( r io.Reader, expectedSize int, opts ...DecodeOption, ) ([]*Response, error)
DecodeBatchResponseFromReader parses a JSON-RPC batch response from an io.Reader.
func DecodeResponse ¶
func DecodeResponse(data []byte, opts ...DecodeOption) (*Response, error)
DecodeResponse parses and returns a new Response from a byte slice. Pass WithAllowJSONRPC1 to accept JSON-RPC 1.0 responses; without it, decoding is strict JSON-RPC 2.0.
func DecodeResponseFromReader ¶
func DecodeResponseFromReader( r io.Reader, expectedSize int, opts ...DecodeOption, ) (*Response, error)
DecodeResponseFromReader parses and returns a new Response from an io.Reader. expectedSize is optional and used for internal buffer sizing; pass 0 if unknown.
func DecodeResponseOrBatch ¶
func DecodeResponseOrBatch( data []byte, opts ...DecodeOption, ) (resps []*Response, isBatch bool, err error)
DecodeResponseOrBatch attempts to parse either a single response or a batch of responses. - For single responses: returns slice with one element - For batch responses: returns slice with multiple elements
func NewErrorResponse ¶
NewErrorResponse creates a JSON-RPC 2.0 error response.
func NewResponse ¶
NewResponse creates a JSON-RPC 2.0 response with a result.
func NewResponseFromRaw ¶
func NewResponseFromRaw(id any, rawResult json.RawMessage) (*Response, error)
NewResponseFromRaw creates a JSON-RPC 2.0 response with a raw result.
func (*Response) Clone ¶ added in v0.8.0
Clone creates a copy of the response with minimal shared state.
Deep copies:
- rawID, rawError, result byte slices
- Error struct
Shallow copies type any fields:
- id field
- Error.Data field
Not copied:
- AST cache (clone starts with empty cache)
- Sync primitives (fresh Once and Mutex created)
func (*Response) Err ¶
Err returns the error from the response. If there is no error, nil is returned.
func (*Response) Free ¶ added in v0.8.0
func (r *Response) Free()
Free releases memory-retaining fields. Only use after consuming the response.
func (*Response) IsEmpty ¶
IsEmpty returns whether the Response can be considered empty.
A JSON-RPC response is considered empty when BOTH the error and result are empty:
- Result is empty if: empty byte slice, null, empty string (""), empty array ([]), empty object ({}), or hex zero value ("0x")
- Error is empty if: nil, or has both code=0 and message=""
func (*Response) MarshalJSON ¶
MarshalJSON serializes the Response into a JSON-RPC 2.0 compliant byte slice.
The method prioritizes parsed fields (id, err) over their raw counterparts (rawID, rawError) when both are present. If only raw fields exist, they are used directly to avoid unnecessary re-marshaling.
func (*Response) PeekBytesByPath ¶ added in v0.8.0
PeekBytesByPath returns raw JSON bytes for a nested field without unmarshaling the entire result. This is useful when you want to extract a sub-object or array and unmarshal it separately.
Example usage to extract a nested transaction object:
txBytes, err := response.PeekBytesByPath("transaction")
The AST node is lazily built on first call and cached for subsequent calls, making repeated field access very efficient.
func (*Response) PeekStringByPath ¶ added in v0.8.0
PeekStringByPath traverses the result JSON using sonic's AST to extract a string field without unmarshaling the entire result. This is valuable for large responses where you only need to access specific nested fields.
The path is specified as a sequence of keys for nested objects. An example using multiple arguments:
from, err := response.PeekStringByPath("transaction", "from")
The AST node is lazily built on first call and cached for subsequent calls, making repeated field access very efficient.
func (*Response) RawResult ¶
func (r *Response) RawResult() json.RawMessage
RawResult returns the raw JSON-encoded result bytes. Note: for string results, this includes the JSON quotes (e.g., "result" not result).
func (*Response) Size ¶ added in v0.8.0
Size returns the approximate serialized size of the response in bytes.
func (*Response) Unmarshal ¶ added in v0.7.2
Unmarshal deserializes the entire Response into a custom struct.
Note: It performs a marshal-then-unmarshal round trip, making it less efficient than using individual getter methods (IDOrNil(), Err(), RawResult(), UnmarshalResult()).
func (*Response) UnmarshalError ¶
UnmarshalError deserializes the raw error bytes into the err field of the Response.
Example ¶
ExampleResponse_UnmarshalError demonstrates handling error responses.
jsonData := []byte(
`{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}`,
)
resp, err := DecodeResponse(jsonData)
if err != nil {
fmt.Printf("Decode error: %v\n", err)
return
}
// For responses with errors, the error is automatically unmarshaled during decode
// Check if response has an error
if resp.Err() != nil {
fmt.Printf("RPC Error %d: %s\n", resp.Err().Code, resp.Err().Message)
}
Output: RPC Error -32601: Method not found
func (*Response) UnmarshalJSON ¶
UnmarshalJSON deserializes JSON-RPC 2.0 response data into the Response. Returns an error if the data is invalid JSON, has an incorrect JSON-RPC version, contains both result and error fields, or contains neither.
func (*Response) UnmarshalResult ¶
UnmarshalResult deserializes the raw Result field into the provided destination.
Example ¶
ExampleResponse_UnmarshalResult demonstrates unmarshaling result into different types.
// Simulate decoding a response with a structured result
jsonData := []byte(`{"jsonrpc":"2.0","id":1,"result":{"balance":1000,"currency":"USD"}}`)
resp, err := DecodeResponse(jsonData)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
// Define target struct
type BalanceResult struct {
Balance int `json:"balance"`
Currency string `json:"currency"`
}
// Unmarshal result
var result BalanceResult
if err := resp.UnmarshalResult(&result); err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Printf("Balance: %d %s\n", result.Balance, result.Currency)
Output: Balance: 1000 USD
Example (Primitives) ¶
ExampleResponse_UnmarshalResult_primitives demonstrates unmarshaling primitive results.
// String result
jsonData := []byte(`{"jsonrpc":"2.0","id":1,"result":"success"}`)
resp, _ := DecodeResponse(jsonData)
var strResult string
if err := resp.UnmarshalResult(&strResult); err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Printf("String: %s\n", strResult)
// Number result
jsonData = []byte(`{"jsonrpc":"2.0","id":2,"result":42}`)
resp, _ = DecodeResponse(jsonData)
var intResult int
if err := resp.UnmarshalResult(&intResult); err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Printf("Number: %d\n", intResult)
// Boolean result
jsonData = []byte(`{"jsonrpc":"2.0","id":3,"result":true}`)
resp, _ = DecodeResponse(jsonData)
var boolResult bool
if err := resp.UnmarshalResult(&boolResult); err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Printf("Boolean: %t\n", boolResult)
Output: String: success Number: 42 Boolean: true
func (*Response) Validate ¶
Validate checks if the Response conforms to the JSON-RPC 2.0 specification. The version field must be exactly "2.0"; this strict check is used on the encode path.
func (*Response) WithID ¶ added in v0.8.1
WithID returns a cloned response with the supplied ID while leaving the original untouched. The clone keeps cached payload slices so callers avoid repeated marshaling work.