Skip to content

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 →