5.2 KiB
STParsedJSON.h — JSON-to-STObject Parsing Interface
Purpose
This header declares STParsedJSONObject, the single public entry point for converting a JSON representation of an XRPL protocol object into the ledger's internal serialized type (STObject). It sits at the boundary between the JSON-based RPC layer and the binary-canonical serialized type (ST) system, performing schema validation and type coercion in a single constructor call.
The class is most prominently used in TransactionSign.cpp, where user-submitted tx_json from an RPC request must be parsed, validated, and converted into a properly typed STTx before signing or simulation can proceed.
Design Philosophy: No-Throw, Public-Member Result
Rather than returning a value or throwing on failure, STParsedJSONObject communicates outcomes through two public data members: std::optional<STObject> object and Json::Value error. The constructor is documented to not throw. All exceptions generated by the deeply recursive parsing logic are caught internally and translated into structured RPC::make_error(rpcINVALID_PARAMS, ...) JSON error values. This is a deliberate design choice for RPC-layer code: callers check parsed.object.has_value() and, on failure, can forward parsed.error directly back to the RPC client with no additional formatting work.
The class is non-copyable and not default-constructible — a parse is a one-shot construction. This enforces that every STParsedJSONObject instance represents exactly one completed parse attempt with a definite success or failure state.
Implementation Structure
The real complexity lives in STParsedJSON.cpp, entirely within the internal STParsedJSONDetail namespace. The public constructor simply calls parseObject(name, json, sfGeneric, 0, error) and assigns the result to object.
parseObject() and parseArray() are mutually recursive. parseObject() iterates over every JSON member, looks each field name up via SField::getField() to validate it exists in the XRPL protocol schema (rejecting unknown fields with rpcINVALID_PARAMS), then dispatches on the field's fieldType:
STI_OBJECT,STI_TRANSACTION,STI_LEDGERENTRY,STI_VALIDATION— recurse intoparseObject()atdepth + 1, building a nestedSTObject.STI_ARRAY— recurse intoparseArray(), which expects each array element to be a single-key JSON object (the XRPL convention for typed array entries), parses the inner object, and appends it to anSTArray.- Everything else — delegated to
parseLeaf(), which handles all scalar and composite leaf types without further recursion.
A depth guard (maxDepth = 64) prevents stack exhaustion from pathologically nested input, returning a too_deep error.
Leaf Type Handling
parseLeaf() is a large switch on field.fieldType covering the full range of XRPL serialized types. Several non-obvious behaviors are worth noting:
STI_UINT16(parseUint16): When parsingsfTransactionTypeorsfLedgerEntryType, string values like"Payment"are resolved viaTxFormats::getInstance().findTypeByName()orLedgerFormats::getInstance().findTypeByName()respectively. ThesfGenerictemplate sentinel is also replaced withsfTransactionorsfLedgerEntryat this point, so thatapplyTemplateFromSField()later applies the correct field template for the transaction or ledger entry type.STI_UINT32(parseUint32):sfPermissionValuefields accept human-readable granular permission names or transaction type names, resolved throughPermission::getInstance().STI_UINT8:sfTransactionResultfields accept TER result code strings (e.g."tesSUCCESS"), resolved throughtransCode().STI_UINT64: Hexadecimal string input by default, but fields with thesMD_BaseTenmetadata flag parse as base-10 decimal (used for amount fields that happen to carry 64-bit integers).STI_ACCOUNT: Accepts either 40-character hex (rawAccountID) or Base58Check-encoded r-addresses, trying hex first.STI_PATHSET: Handles the nested path structure with special support for both IOU paths (currency/issuer) and MPT (Multi-Purpose Token) paths (mpt_issuance_id), validating that the MPT issuer in a path element is consistent with the issuance ID.
Template Application
After building an STObject from all JSON members, parseObject() calls data.applyTemplateFromSField(inName). This enforces field templates — inner object types in the XRPL protocol have required and optional field sets, and this call validates the parsed object matches the expected template for its type. A mismatch throws STObject::FieldErr, caught and translated into a template_mismatch error.
Error Reporting
The internal STParsedJSONDetail namespace defines a set of small helper functions (not_an_object, unknown_field, bad_type, out_of_range, invalid_data, too_deep, etc.) that construct rpcINVALID_PARAMS error Json::Value objects with dot-separated field path names (e.g. "tx_json.Signers[0].Signer.Account"). This precise path information in error messages is generated by threading the json_name string through recursive calls, accumulating field names at each level — making validation failures actionable for RPC clients.