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
- type Error
- type Request
- func DecodeBatchRequest(data []byte) ([]*Request, error)
- func DecodeBatchRequestFromReader(r io.Reader, expectedSize int) ([]*Request, error)
- func DecodeRequest(data []byte) (*Request, error)
- func DecodeRequestOrBatch(data []byte) ([]*Request, bool, 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 RequestFromBytes(data []byte) (*Request, error)
- 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) ([]*Response, error)
- func DecodeBatchResponseFromReader(r io.Reader, expectedSize int) ([]*Response, error)
- func DecodeResponse(data []byte) (*Response, error)
- func DecodeResponseFromReader(r io.Reader, expectedSize int) (*Response, error)
- func DecodeResponseOrBatch(data []byte) ([]*Response, bool, error)
- func NewErrorResponse(id any, err *Error) *Response
- func NewResponse(id any, result any) (*Response, error)
- func NewResponseFromBytes(data []byte) (*Response, error)
- func NewResponseFromRaw(id any, rawResult json.RawMessage) (*Response, error)
- func NewResponseFromStream(body io.ReadCloser, expectedSize int) (*Response, error)
- func (r *Response) Equals(other *Response) bool
- func (r *Response) Err() *Error
- func (r *Response) IDOrNil() any
- func (r *Response) IDRaw() any
- func (r *Response) IDString() string
- func (r *Response) IsEmpty() bool
- func (r *Response) MarshalJSON() ([]byte, error)
- func (r *Response) RawResult() json.RawMessage
- func (r *Response) String() string
- 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
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 (JSON array). 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 (JSON array). 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. Uses math/rand/v2 which is automatically seeded and provides good randomness.
Types ¶
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 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, with minor exceptions. E.g. the ID field is allowed to be fractional in this implementation.
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 ¶
DecodeBatchRequest parses a JSON-RPC batch request from a byte slice. Returns an error if: - Input is not a JSON array - Array is empty (per JSON-RPC 2.0 spec: "The Server should respond with an error") - Any element fails to parse as a valid Request
func DecodeBatchRequestFromReader ¶
DecodeBatchRequestFromReader parses a JSON-RPC batch request from an io.Reader.
func DecodeRequest ¶
DecodeRequest parses a JSON-RPC request from a byte slice.
func DecodeRequestOrBatch ¶
DecodeRequestOrBatch attempts to parse either a single request or a batch of requests. Returns (requests, isBatch, error). - For single requests: returns slice with one element, isBatch=false - For batch requests: returns slice with multiple elements, isBatch=true - Empty batches are rejected per JSON-RPC 2.0 spec
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 RequestFromBytes ¶
RequestFromBytes creates a JSON-RPC request from a byte slice. Deprecated: Use DecodeRequest instead. See MIGRATION.md for details. Will be removed in v2.0.
func (*Request) IsEmpty ¶
IsEmpty returns whether the Request can be considered 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 a JSON-RPC request.
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. The function takes two custom actions; sets the JSON-RPC version to 2.0 and unmarshals the ID separately, to handle both string and float64 IDs.
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. All fields are unexported to enforce immutability. Use getter methods to access field values.
The Response type uses lazy unmarshaling for the ID and Error fields to optimize performance. These fields are unmarshaled on first access via IDOrNil() or Err() respectively.
func DecodeBatchResponse ¶
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 ¶
DecodeBatchResponseFromReader parses a JSON-RPC batch response from an io.Reader.
func DecodeResponse ¶
DecodeResponse parses and returns a new Response from a byte slice.
func DecodeResponseFromReader ¶
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 ¶
DecodeResponseOrBatch attempts to parse either a single response or a batch of responses. Returns (responses, isBatch, error).
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 NewResponseFromBytes ¶
NewResponseFromBytes parses and returns a new Response from a byte slice. Deprecated: Use DecodeResponse instead. See MIGRATION.md for details. Will be removed in v2.0.
func NewResponseFromRaw ¶
func NewResponseFromRaw(id any, rawResult json.RawMessage) (*Response, error)
NewResponseFromRaw creates a JSON-RPC 2.0 response with a raw result.
func NewResponseFromStream ¶
func NewResponseFromStream(body io.ReadCloser, expectedSize int) (*Response, error)
NewResponseFromStream parses and returns a new Response from a stream. Deprecated: Use DecodeResponseFromReader instead. Note that DecodeResponseFromReader does NOT automatically close the reader. See MIGRATION.md for details. Will be removed in v2.0.
func (*Response) Equals ¶
Equals compares the contents of two JSON-RPC responses. This method handles both eagerly and lazily unmarshaled responses by ensuring both IDs and Errors are unmarshaled before comparison.
func (*Response) Err ¶
Err returns the error from the response, if any. The error is unmarshaled lazily on first call and cached for subsequent calls. This method is safe for concurrent use.
func (*Response) IDOrNil ¶
IDOrNil returns the unmarshaled ID, or nil if unmarshaling fails. The ID is unmarshaled lazily on first call and cached for subsequent calls. This method is safe for concurrent use.
func (*Response) IDRaw ¶
IDRaw returns the unmarshaled ID, or nil if unmarshaling fails. Deprecated: Use IDOrNil instead for clearer intent. See MIGRATION.md for details.
func (*Response) IsEmpty ¶
IsEmpty returns whether the JSON-RPC response can be considered empty.
This method is primarily used to detect responses that carry no meaningful data, such as responses from notification requests (which shouldn't exist per spec) or placeholder responses.
A 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=""
The specific byte pattern checks (null, "0x", etc.) handle common JSON-RPC conventions where these values represent "no data" semantically.
func (*Response) MarshalJSON ¶
MarshalJSON marshals a JSON-RPC response into a byte slice. The public members ID and Error will be prioritized over their raw counterparts.
func (*Response) RawResult ¶
func (r *Response) RawResult() json.RawMessage
RawResult returns the raw JSON-encoded result bytes. For string results, this includes the JSON quotes (e.g., "result" not result). Use UnmarshalResult to decode the result into a specific type.
func (*Response) UnmarshalError ¶
UnmarshalError unmarshals the raw error into the Error field. The error is unmarshaled lazily on first call and cached for subsequent calls. This method is safe for concurrent use.
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 unmarshals the input data into the members of Response. Note: does not unmarshal the Result field, but leaves that at the caller's discretion (UnmarshalResult). This is an optimization to prevent unnecessary unmarshalling of the Result field for very large blobs.
func (*Response) UnmarshalResult ¶
UnmarshalResult decodes the raw Result field into the provided destination pointer.
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