JSON-RPC Request Builder — C++ source
Build valid JSON-RPC 2.0 requests, notifications, success responses, and error responses, plus batch arrays. Validate message structure.
This is the C++ implementation — the same logic the interactive tool runs, in a shareable, citable form.
// json-rpc-builder — C++ port: JSON-RPC 2.0 message builder + structural validation.
// Values are pre-encoded JSON fragments; `obj` assembles them with ordered keys,
// so output matches the TS lib without a JSON dependency.
#include <cstdio>
#include <initializer_list>
#include <map>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
struct Outcome { bool ok; std::string json; std::string error; };
static std::string obj(std::initializer_list<std::pair<std::string_view, std::string_view>> kv) {
std::string out = "{";
for (const auto& [k, v] : kv) out += "\"" + std::string(k) + "\":" + std::string(v) + ",";
if (out.back() == ',') out.pop_back();
return out += "}";
}
// Messages for the error codes the JSON-RPC 2.0 spec reserves (§5.1).
static std::string_view standard_error(int code) {
switch (code) {
case -32700: return "Parse error"; case -32600: return "Invalid Request";
case -32601: return "Method not found"; case -32602: return "Invalid params";
case -32603: return "Internal error"; case -32000: return "Server error";
default: return "Error";
}
}
static Outcome build_request(std::string_view method, const char* params, std::string_view id) {
if (method.empty()) return {false, "", "method must be a non-empty string"};
std::string m = "\"" + std::string(method) + "\"", i = std::string(id);
return params ? Outcome{true, obj({{"jsonrpc", "\"2.0\""}, {"method", m},
{"params", params}, {"id", i}}), ""}
: Outcome{true, obj({{"jsonrpc", "\"2.0\""}, {"method", m}, {"id", i}}), ""};
}
// A notification is a request without an id: fire-and-forget, no reply.
static Outcome build_notification(std::string_view method, const char* params) {
if (method.empty()) return {false, "", "method must be a non-empty string"};
std::string m = "\"" + std::string(method) + "\"";
return params ? Outcome{true, obj({{"jsonrpc", "\"2.0\""}, {"method", m},
{"params", params}}), ""}
: Outcome{true, obj({{"jsonrpc", "\"2.0\""}, {"method", m}}), ""};
}
static Outcome build_error_response(std::string_view id, int code,
const char* message, const char* data) {
std::string_view msg = message ? message : standard_error(code);
std::string m = "\"" + std::string(msg) + "\"";
auto err = data ? obj({{"code", std::to_string(code)}, {"message", m}, {"data", data}})
: obj({{"code", std::to_string(code)}, {"message", m}});
return {true, obj({{"jsonrpc", "\"2.0\""}, {"error", err}, {"id", std::string(id)}}), ""};
}
// Structural check of one parsed message, represented as field -> JSON fragment.
// A map plays the role of the parsed JSON object; the method type check reads
// the fragment (a JSON string starts with a quote).
static std::vector<std::string> validate(const std::map<std::string, std::string>& f) {
std::vector<std::string> errors;
auto it = f.find("jsonrpc");
if (it == f.end() || it->second != "\"2.0\"") errors.push_back("jsonrpc must be \"2.0\".");
if (f.count("method") && f.at("method").front() != '"')
errors.push_back("method must be a string.");
if (f.count("result") && f.count("error"))
errors.push_back("cannot have both result and error.");
if (!f.count("method") && !f.count("result") && !f.count("error"))
errors.push_back("must have method, result, or error.");
return errors;
}
int main() {
printf("%s\n", build_request("getBalance", R"(["0x1234"])", "1").json.c_str());
printf("%s\n", build_notification("blockHeader", nullptr).json.c_str());
printf("%s\n", build_error_response("1", -32601, nullptr, nullptr).json.c_str());
for (const auto& e : validate({{"jsonrpc", "\"1.0\""}})) printf("error: %s\n", e.c_str());
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →