Problem statement
Composing outbound JSON-RPC requests currently requires manual JsonNode or raw JSON assembly. This makes it easy to introduce structural mistakes around jsonrpc, id, notification vs request semantics, and params shape, while also repeating the same payload-building code across integrations.
The current JsonRpcRequest type is not the right user-facing composition target for this problem. It is primarily a parser output model used inside the dispatcher pipeline, with fields such as idPresent and source that encode parsing semantics rather than outbound payload authoring intent.
By contrast, JsonRpcResponse already exposes clear static factories for success and error responses, so response-side builders are not necessary for this issue.
Proposed solution
Add request payload builders in jsonrpc-core for outbound JSON-RPC composition:
JsonRpcRequestBuilder
JsonRpcRequestBatchBuilder
Design goals:
- explicit request vs notification entry points
- RFC-friendly default
jsonrpc: "2.0"
- fail-fast behavior for invalid builder states
- transport-agnostic Jackson
ObjectNode / ArrayNode output via buildNode()
Expected builder behavior:
request(method) requires an id before buildNode()
notification(method) must reject any id(...) call
- batch builder must reject empty batches
- params helpers should support both object and array composition without requiring raw JSON strings
Proposed API shape:
JsonRpcRequestBuilder.request("subtract")
JsonRpcRequestBuilder.notification("heartbeat")
JsonRpcRequestBuilder
.id(long value)
.id(String value)
.id(JsonNode value)
.nullId()
.params(JsonNode value)
.paramsArray(JsonNode... elements)
.paramsObject(Consumer<ObjectNode> configurator)
.buildNode()
JsonRpcRequestBatchBuilder
.add(JsonRpcRequestBuilder builder)
.addRequest(String method, Consumer<JsonRpcRequestBuilder> customizer)
.addNotification(String method, Consumer<JsonRpcRequestBuilder> customizer)
.buildNode()
Notes for the API contract:
JsonRpcRequestBuilder.request(...) must throw on buildNode() when no id has been set.
JsonRpcRequestBuilder.notification(...) must throw immediately when any id(...) or nullId() method is called.
- calling
params(...), paramsArray(...), or paramsObject(...) more than once on the same builder must throw immediately.
paramsObject(...) should create an ObjectNode internally and expose it to the configurator.
paramsArray(...) should create an ArrayNode from the provided elements.
buildNode() should return ObjectNode for single request/notification builders and ArrayNode for batch builders.
Also add a supporting convenience overload to JsonRpcError:
JsonRpcError.of(int code, String message, JsonNode data)
This covers the only missing response-side convenience currently identified, without introducing a separate response builder abstraction.
Alternatives considered
- Keep the original broader scope with request and response builders
- Expose
buildModel() returning JsonRpcRequest
- Continue using documentation-only snippets or user-defined helper classes
These approaches are less aligned with the current codebase design. JsonRpcRequest is a parser-oriented internal model, not an outbound composition API, and JsonRpcResponse already has adequate factory methods for normal response construction.
JSON-RPC behavior impact
No protocol behavior change. This adds outbound payload composition utilities and a small error-object factory convenience.
Additional context
- Keep the implementation Spring-independent and limited to
jsonrpc-core.
- Build nodes directly with
JsonNodeFactory.instance; do not require an ObjectMapper.
- Update
JsonRpcRequest Javadoc to clarify that it is primarily a parsed internal model.
- Add unit tests for request/notification distinction, required-id behavior, notification id rejection, params composition, and empty-batch rejection.
- Add documentation examples in README and pure Java guides for outbound request composition.
- Response builders and response batch builders are intentionally out of scope for this issue.
Problem statement
Composing outbound JSON-RPC requests currently requires manual
JsonNodeor raw JSON assembly. This makes it easy to introduce structural mistakes aroundjsonrpc,id, notification vs request semantics, and params shape, while also repeating the same payload-building code across integrations.The current
JsonRpcRequesttype is not the right user-facing composition target for this problem. It is primarily a parser output model used inside the dispatcher pipeline, with fields such asidPresentandsourcethat encode parsing semantics rather than outbound payload authoring intent.By contrast,
JsonRpcResponsealready exposes clear static factories for success and error responses, so response-side builders are not necessary for this issue.Proposed solution
Add request payload builders in
jsonrpc-corefor outbound JSON-RPC composition:JsonRpcRequestBuilderJsonRpcRequestBatchBuilderDesign goals:
jsonrpc: "2.0"ObjectNode/ArrayNodeoutput viabuildNode()Expected builder behavior:
request(method)requires anidbeforebuildNode()notification(method)must reject anyid(...)callProposed API shape:
Notes for the API contract:
JsonRpcRequestBuilder.request(...)must throw onbuildNode()when no id has been set.JsonRpcRequestBuilder.notification(...)must throw immediately when anyid(...)ornullId()method is called.params(...),paramsArray(...), orparamsObject(...)more than once on the same builder must throw immediately.paramsObject(...)should create anObjectNodeinternally and expose it to the configurator.paramsArray(...)should create anArrayNodefrom the provided elements.buildNode()should returnObjectNodefor single request/notification builders andArrayNodefor batch builders.Also add a supporting convenience overload to
JsonRpcError:JsonRpcError.of(int code, String message, JsonNode data)This covers the only missing response-side convenience currently identified, without introducing a separate response builder abstraction.
Alternatives considered
buildModel()returningJsonRpcRequestThese approaches are less aligned with the current codebase design.
JsonRpcRequestis a parser-oriented internal model, not an outbound composition API, andJsonRpcResponsealready has adequate factory methods for normal response construction.JSON-RPC behavior impact
No protocol behavior change. This adds outbound payload composition utilities and a small error-object factory convenience.
Additional context
jsonrpc-core.JsonNodeFactory.instance; do not require anObjectMapper.JsonRpcRequestJavadoc to clarify that it is primarily a parsed internal model.