Files
rippled/include/xrpl/protocol/STParsedJSON.h.ai.md
2026-05-18 22:59:19 +02:00

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 into parseObject() at depth + 1, building a nested STObject.
  • STI_ARRAY — recurse into parseArray(), 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 an STArray.
  • 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 parsing sfTransactionType or sfLedgerEntryType, string values like "Payment" are resolved via TxFormats::getInstance().findTypeByName() or LedgerFormats::getInstance().findTypeByName() respectively. The sfGeneric template sentinel is also replaced with sfTransaction or sfLedgerEntry at this point, so that applyTemplateFromSField() later applies the correct field template for the transaction or ledger entry type.
  • STI_UINT32 (parseUint32): sfPermissionValue fields accept human-readable granular permission names or transaction type names, resolved through Permission::getInstance().
  • STI_UINT8: sfTransactionResult fields accept TER result code strings (e.g. "tesSUCCESS"), resolved through transCode().
  • STI_UINT64: Hexadecimal string input by default, but fields with the sMD_BaseTen metadata flag parse as base-10 decimal (used for amount fields that happen to carry 64-bit integers).
  • STI_ACCOUNT: Accepts either 40-character hex (raw AccountID) 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.