Loading…
Loading…
Technical reference for the SOAP Message Mapper, which transforms JSON to SOAP and SOAP to JSON, with extensive data conversion and WS-Security options.
The SoapMessageMapper is a runtime service that provides bidirectional transformation between JSON payloads and SOAP 1.1/1.2 XML envelopes. It serves as a bridge, enabling REST/JSON API consumers to interact with backend SOAP services.
The primary functions of this component are:
rpc and document binding styles, and injecting standard or custom SOAP headers.UsernameToken (with password text or digest), BinarySecurityToken (e.g., X.509), and complex XML-DSIG signatures.The SoapMessageMapper is configured on a per-request basis using ThreadLocal variables. A calling component, such as a runtime controller, resolves system-level, operation-level, and field-level settings and uses the public setResolved... methods to configure the mapper's behavior for a single transaction.
The SoapMessageMapper uses a combination of WSDL-derived contract mappings and configurable heuristics to perform transformations.
The buildEnvelopeFromJson method orchestrates the creation of a SOAP request.
Map, List, String, etc.). To preserve precision, numbers are parsed as BigDecimal or BigInteger._soapHeaders object in the root of the JSON payload is extracted. Its values are used to populate declared soap:header blocks. The _soapHeaders key is then stripped from the payload to prevent it from being serialized into the soap:Body.contract is provided, the encodeRequestNode method recursively traverses the contract and the JSON payload, transforming the JSON into a Java object structure that mirrors the target XML. This process enforces contract rules, such as choice groups and data constraints, and applies request-side data handling for booleans and nulls.Document is created.soapenv:Envelope, soapenv:Header, and soapenv:Body elements are created.soap:header declarations (operationHeaders), the corresponding values from the _soapHeaders sidecar are serialized into the soapenv:Header.soapenv:Body depends on the WSDL binding style:
document (default): The body's direct child is the message element (e.g., <GreetRequest>).rpc: The body contains a wrapper element named after the operation (e.g., <Greet>), which in turn contains the parameter elements.appendValue method recursively walks the transformed Java object structure and creates the corresponding XML elements and attributes within the soapenv:Body. This step also handles xsi:nil="true" for null values and can qualify child elements with a namespace if the contract specifies elementFormDefault="qualified".The parseSoapResponse method transforms a backend SOAP response into a JSON-like structure. The process is illustrated in the diagram below.
flowchart TD
subgraph SOAP to JSON Flow
A[SOAP Response XML] --> B{Parse XML};
B --> C{"Repair missing namespaces?"};
C -- Yes --> D[Inject standard xmlns];
C -- No --> E;
D --> E[Create DOM];
E --> F{Fault element present?};
F -- Yes --> G[Extract Fault Details];
F -- No --> H{RPC/encoded style?};
H -- Yes --> I[Throw UnsupportedSoapFormatException];
H -- No --> J[Capture SOAP Headers];
J --> K[Select Payload Root];
K --> L{Response Contract provided?};
L -- Yes --> M[Apply Contract Mapping (`decodeResponseNode`)];
L -- No --> N[Apply Heuristics (`elementToValue`)];
subgraph M
direction LR
M1[Data Conversion]
M2[Constraint Validation]
M3[Null/Empty Handling]
end
M --> O[Apply Field Casing & Ordering];
N --> O;
O --> P[Return Success/Fault Mapping];
G --> P;
end
I --> Q((Error));Document. The parser first attempts a direct parse. If it fails due to a namespace error, parseSoapXmlWithNamespaceRepair attempts to inject missing standard xmlns declarations (e.g., for soapenv, xsi) and retries the parse.<soap:Fault> element in the soap:Body or soap:Header. If found, extractSoapFaultDetails parses its contents (supporting both SOAP 1.1 and 1.2 structures) into a map, which is returned in a SoapResponseMapping.soap:encodingStyle) and throws an UnsupportedSoapFormatException if so, as this format is not supported.soap:Header are captured, filtered by allow/deny lists, and stored for the caller.contract is provided, applyContractResponseMapping is used. It calls decodeResponseNode to recursively walk the contract and the DOM, transforming the XML into a Java object structure. This path applies the full suite of configured data conversions for datetimes, numbers, booleans, nulls, arrays, and more.elementToValue performs a heuristic-based transformation. It converts elements to maps and lists, handles xsi:nil, and resolves multi-ref href attributes. It also applies a wrapper-flattening heuristic to simplify list structures.applyFieldCasingRecursively transforms JSON key names to the configured case (e.g., camelCase, snake_case).applyFieldOrderingRecursively sorts the keys in each JSON object according to the configured mode (as-schema, alphabetical, preserve-source).SoapResponseMapping object.The injectWsSecurity... methods modify a SOAP envelope string by parsing it into a DOM, adding a wsse:Security header, and injecting the specified tokens and/or signature.
injectWsSecurityUsernameToken adds a wsse:UsernameToken with credentials.injectWsSecurityBinaryToken adds a wsse:BinarySecurityToken.injectWsSecuritySignedBinaryToken is the most complex, performing a full XML-DSIG signing process. It constructs Reference elements for the parts to be signed (e.g., soap:Body, wsu:Timestamp), builds a SignedInfo block, creates a KeyInfo block according to the specified style (e.g., DirectReference, ThumbprintSHA1), and uses XMLSignatureFactory to generate and insert the ds:Signature.To optimize performance, the SoapMessageMapper uses an internal LRU cache, compiledNodeCache, for WSDL contract metadata. When a contract-driven transformation occurs, the parsed and structured contract nodes (NodeMetadata) are cached. Subsequent requests using the same contract can retrieve the compiled metadata from the cache, avoiding the cost of recompiling it. The cache key (IdentityKey) uses reference equality, making lookups fast. The cache is bounded to a maximum of 500 entries.
The behavior of the SoapMessageMapper is controlled on a per-request basis by setting ThreadLocal variables. The caller uses the public setResolved... methods before invoking a transformation and must call the corresponding clear... method (or clearAllConversionDefaults) in a finally block to prevent state from leaking between requests.
The SOAP body structure is determined by the WSDL binding style, which can be either document or rpc.
| Method | Description |
|---|---|
setBindingStyle(String style) | Sets the binding style for the current request. |
clearBindingStyle() | Clears the binding style for the current thread. |
| Style | Default | Description |
|---|---|---|
document | Yes | The soap:Body's direct child is the message element defined in the WSDL schema. |
rpc | No | The soap:Body's direct child is a wrapper element named after the WSDL operation. For responses, the mapper unwraps this layer. |
For request generation, custom SOAP headers can be supplied in the JSON payload under a reserved _soapHeaders key. These values are used to populate soap:header blocks declared in the operation's WSDL.
| Method | Description |
|---|---|
setOperationHeaders(List<OperationHeaderRecord> headers) | Provides the list of WSDL-declared soap:header blocks for the current operation. |
clearOperationHeaders() | Clears the header declarations for the current thread. |
The _soapHeaders key must be a JSON object at the root of the request payload. Each key in this object corresponds to the local name of a declared header block.
{
"_soapHeaders": {
"SessionID": "abc-123",
"TransactionContext": {
"ID": "xyz-789"
}
},
"payload": {
"user": "test"
}
}
The following sections detail the configuration options for transforming data from a SOAP response to JSON. Each feature is controlled by a resolvedMode (the default for the operation) and a fieldOverrides map (for per-field exceptions).
Controls how arrays with a single element are represented in the output JSON.
| Method | Description |
|---|---|
setResolvedArrayCollapseHandling(String resolvedMode, Map<String, String> fieldOverrides) | Sets the array collapse behavior. |
clearArrayCollapseHandling() | Clears the array collapse settings. |
| Mode | Default | Description |
|---|---|---|
always-array | Yes | Arrays are always emitted as JSON arrays, even if they contain only one element. |
collapse-when-single | No | An array with exactly one element is emitted as that single element, not as an array. |
Controls how numeric values from SOAP are represented in the output JSON.
| Method | Description |
|---|---|
setResolvedNumericHandling(String resolvedMode, Map<String, String> fieldOverrides) | Sets the numeric handling behavior. |
clearNumericHandling() | Clears the numeric handling settings. |
| Mode | Default | Description |
|---|---|---|
as-number | Yes | Numeric values are emitted as JSON numbers. |
as-string | No | All numeric values are emitted as JSON strings, preserving the original lexical representation from the SOAP payload. |
as-string-when-unsafe | No | Emits values as JSON numbers, unless the type is an unbounded-precision XSD type (e.g., xsd:long, xsd:decimal) or its absolute value exceeds JavaScript's MAX_SAFE_INTEGER (9007199254740991), in which case it is emitted as a JSON string. |
Controls how whitespace in string-based XSD types (e.g., xsd:string, xsd:token) is handled.
| Method | Description |
|---|---|
setResolvedWhitespaceHandling(String resolvedMode, Map<String, String> fieldOverrides) | Sets the whitespace handling behavior. |
clearWhitespaceHandling() | Clears the whitespace handling settings. |
| Mode | Default | Description |
|---|---|---|
preserve | Yes | Whitespace is preserved as it appeared in the SOAP payload. |
trim | No | Leading and trailing whitespace is removed. |
collapse | No | Leading and trailing whitespace is removed, and any sequence of internal whitespace characters is replaced by a single space. |
Controls the encoding of xsd:base64Binary and xsd:hexBinary fields in the output JSON.
| Method | Description |
|---|---|
setResolvedBinaryHandling(String resolvedMode, String resolvedDataUrlMime, Map<String, Object> fieldOverrides) | Sets the binary data handling behavior. The fieldOverrides map can contain structured objects with mode and dataUrlMime keys. |
clearBinaryHandling() | Clears the binary handling settings. |
| Mode | Default | Description |
|---|---|---|
passthrough | Yes | The raw lexical value (Base64 or hex string) is passed through to the JSON. |
base64 | No | The value is encoded as a Base64 string. If the source is hexBinary, it is first decoded from hex and then re-encoded to Base64. |
hex | No | The value is encoded as a lowercase hex string. If the source is base64Binary, it is first decoded from Base64 and then re-encoded to hex. |
data-url | No | The value is encoded as a data: URL. The binary data is Base64-encoded. The MIME type defaults to application/octet-stream but can be overridden by resolvedDataUrlMime or a per-field dataUrlMime override. |
WARNING
If the source binary data is malformed (e.g., invalid Base64 or hex characters), the transformation for that field will fail, and the original, untransformed value will be returned. A warning (S2R-RUN-0440) is logged in this case.
Controls the formatting of xsd:duration fields.
| Method | Description |
|---|---|
setResolvedDurationHandling(String resolvedMode, Map<String, String> fieldOverrides) | Sets the duration formatting behavior. |
clearDurationHandling() | Clears the duration handling settings. |
| Mode | Default | Description |
|---|---|---|
passthrough | Yes | The raw ISO 8601 duration string (e.g., P1Y2M3DT4H5M6S) is passed through. |
seconds | No | The duration is converted to a total number of seconds. Year and month components are approximated (1Y=365D, 1M=30D). The result is a JSON number (integer or float). |
components | No | The duration is deconstructed into a JSON object with keys years, months, days, hours, minutes, seconds, and an optional negative: true. |
Controls the formatting of xsd:date, xsd:dateTime, and xsd:time fields.
| Method | Description |
|---|---|
setResolvedDatetimeHandling(...) | Sets the datetime formatting behavior. Accepts format, customPattern, timezone, localTimezone, and a fieldOverrides map. |
clearDatetimeHandling() | Clears the datetime handling settings. |
Format Modes
| Mode | Description |
|---|---|
iso-8601-utc | (Default) Formats as an ISO 8601 string in UTC. Uses Z for UTC timezone. |
iso-8601-offset | Formats as an ISO 8601 string with a timezone offset (e.g., +01:00). |
iso-8601-local | Formats as an ISO 8601 string with no timezone information. |
epoch-seconds | Formats as the number of seconds from the Unix epoch. |
epoch-millis | Formats as the number of milliseconds from the Unix epoch. |
custom | Formats using a custom pattern provided in the customPattern parameter. |
Timezone Modes
| Mode | Description |
|---|---|
utc | (Default) Converts the time to the UTC timezone before formatting. |
local | Converts the time to the timezone specified in the localTimezone parameter. |
passthrough | Preserves the original timezone from the SOAP payload. |
Controls how null or empty values are represented in both request and response transformations. There are separate settings for the request and response directions.
| Method | Description |
|---|---|
setResolvedNullRequestHandling(...) | Sets null handling for JSON-to-SOAP requests. |
setResolvedNullResponseHandling(...) | Sets null handling for SOAP-to-JSON responses. |
| Mode | Request Default | Response Default | Description |
|---|---|---|---|
omit-field | omit-field | The field is omitted from the output object. | |
as-null | as-null | The field is emitted with a JSON null value or an XML element with xsi:nil="true". | |
as-empty-value | The field is emitted with an "empty" value appropriate for its type (e.g., "" for string, [] for array, {} for object, 0 for number, false for boolean). |
IMPORTANT
For xsd:dateTime and related types, a separate datetimeNullMode (omit or null) takes precedence over the general null handling mode for responses. This is configured via setResolvedDatetimeHandling.
Controls the case of keys in the output JSON object.
| Method | Description |
|---|---|
setResolvedFieldCasing(String resolvedMode, Map<String, String> fieldOverrides) | Sets the field casing behavior. fieldOverrides maps a soapXPath to an explicit JSON key name. |
clearFieldCasing() | Clears the field casing settings. |
| Mode | Default | Description |
|---|---|---|
as-source | Yes | Field names are preserved exactly as they appear in the source WSDL or SOAP element. |
camelCase | No | Field names are converted to camelCase. |
snake_case | No | Field names are converted to snake_case. |
kebab-case | No | Field names are converted to kebab-case. |
PascalCase | No | Field names are converted to PascalCase. |
Controls the order of keys in the output JSON object.
| Method | Description |
|---|---|
setResolvedFieldOrdering(String resolvedMode) | Sets the field ordering behavior. |
clearFieldOrdering() | Clears the field ordering settings. |
| Mode | Default | Description |
|---|---|---|
as-schema | Yes | Fields are ordered as they appear in the WSDL contract. |
alphabetical | No | Fields are sorted alphabetically (case-insensitive) at every level of the JSON object. The _meta key is always pinned to the first position. |
preserve-source | No | Fields are ordered to match the element order in the source SOAP DOM. |
xsi:type Discriminator HandlingControls how xsi:type attributes on polymorphic elements are handled in the output JSON.
| Method | Description |
|---|---|
setResolvedXsiTypeHandling(String resolvedMode, String discriminatorFieldName, Map<String, String> fieldOverrides) | Sets the xsi:type handling behavior. |
clearXsiTypeHandling() | Clears the xsi:type handling settings. |
| Mode | Default | Description |
|---|---|---|
strip | Yes | The xsi:type attribute is ignored and not represented in the JSON. |
keep-as-meta | No | The xsi:type value is added to the JSON object under a _xsiType key. |
discriminator-field | No | A new field is added to the JSON object. The field's name is specified by discriminatorFieldName (default type), and its value is the local name from the xsi:type. |
setResolvedRequiredHandling controls behavior when a required field is present but empty in the SOAP response. The default permissive mode allows it, while fail-with-502 causes a RequiredFieldEmptyException.setResolvedHeaderPropagation controls whether SOAP headers from the response are captured. The drop mode ignores them. The expose-as-meta mode places them in a _meta object in the JSON response. expose-as-http-headers is also a mode, though its implementation is handled by the caller.The SoapMessageMapper exposes several public methods for transformation and WS-Security injection.
buildEnvelopeFromJson(...)Builds a SOAP envelope from a JSON payload. Several overloads exist; the most comprehensive signature allows for fine-grained control over data handling.
public String buildEnvelopeFromJson(
String operationName,
String namespace,
String soapVersion,
String jsonPayload,
Map<String, Object> requestMapping,
Map<String, String> fieldEncodings,
String resolvedBoolMode,
Map<String, String> fieldBooleanOverrides,
String resolvedNullRequestModeValue,
Map<String, String> fieldNullOverrides
);
operationName: The name of the SOAP operation.namespace: The target namespace for the operation element.soapVersion: "1.1" or "1.2".jsonPayload: The request payload as a JSON string.requestMapping: The WSDL-derived contract or rule-based mapping.fieldEncodings: Per-field encoding rules for the request.resolvedBoolMode: Default boolean handling ("as-bool" or "as-int").fieldBooleanOverrides: Per-field boolean handling overrides.resolvedNullRequestModeValue: Default null handling for the request.fieldNullOverrides: Per-field null handling overrides.Returns: A String containing the complete SOAP envelope.
parseSoapResponse(...)Parses a SOAP response into a SoapResponseMapping object.
public SoapResponseMapping parseSoapResponse(
String xml,
Map<String, Object> responseMapping,
Map<String, String> fieldEncodings,
String resolvedBoolMode,
Map<String, String> fieldBooleanOverrides,
String resolvedNullResponseModeValue,
Map<String, String> fieldNullOverrides
);
xml: The SOAP response envelope as an XML string.responseMapping: The WSDL-derived contract or rule-based mapping.fieldEncodings: Per-field decoding rules for the response.resolvedBoolMode: Default boolean handling.fieldBooleanOverrides: Per-field boolean handling overrides.resolvedNullResponseModeValue: Default null handling for the response.fieldNullOverrides: Per-field null handling overrides.Returns: A SoapResponseMapping object, which contains a boolean fault flag and the payload as a Map<String, Object>.
injectWsSecurityUsernameToken(...)Injects a wsse:UsernameToken into a SOAP envelope.
public String injectWsSecurityUsernameToken(
String soapEnvelope,
String username,
String password,
boolean passwordDigest,
boolean includeTimestamp,
// ... and many other optional parameters for fine-tuning
);
This method supports numerous optional parameters to control aspects like wsu:Id attributes, mustUnderstand headers, element order, and timestamp creation/TTL.
injectWsSecuritySignedBinaryToken(...)Injects a wsse:BinarySecurityToken and an XML-DSIG Signature into a SOAP envelope. This is the most complex injection method, with a large number of parameters to control every aspect of the signature creation process.
| Parameter | Description |
|---|---|
soapEnvelope | The input SOAP envelope string. |
tokenValue | The binary security token value (e.g., Base64-encoded certificate). |
signingKeyPem | The private key in PEM format for signing. |
signingKeyPassword | The password for the encrypted private key. |
includeTimestamp | Whether to include and sign a wsu:Timestamp element. |
referenceStyle | The KeyInfo reference style (e.g., direct_reference, thumbprint_sha1, issuer_serial). |
signatureAlgorithm | The XML-DSIG signature algorithm URI (e.g., http://www.w3.org/2001/04/xmldsig-more#rsa-sha256). |
digestAlgorithm | The XML-DSIG digest algorithm URI (e.g., http://www.w3.org/2001/04/xmlenc#sha256). |
canonicalizationMethod | The XML-DSIG canonicalization method URI (e.g., http://www.w3.org/2001/10/xml-exc-c14n#). |
signedParts | A list of parts to sign, such as body or timestamp. |
keyInfoMode | The structure of the KeyInfo element (e.g., security_token_reference, key_value). |
... | Many other parameters for IDs, overrides, and algorithm-specific details. |
| Exception / Condition | Trigger | Meaning |
|---|---|---|
IllegalArgumentException | Invalid input, such as malformed JSON/XML, missing credentials for WS-Security, or a value that violates a contract constraint (pattern, enum, length, etc.). | The request could not be processed due to invalid data provided by the caller or a contract violation. The message contains details about the path and nature of the error. |
ContractDriftException | The structure of the SOAP payload does not match the provided WSDL contract. | The backend service may have changed its interface, and the contract is now out of date. |
UnsupportedSoapFormatException | The SOAP response uses a format that is not supported, such as RPC/Section 5 encoding (soap:encodingStyle). | The platform does not support decoding this particular SOAP wire format. The error code S2R-RUN-0415 is associated with this. |
RequiredFieldEmptyException | A field marked as required in the contract was present but empty in the SOAP response, and the required-but-empty handling mode was set to fail-with-502. | The backend returned a response that violates the contract by not providing a value for a required field. |
S2R-RUN-0440 binary-decode-failed (log) | An error occurred while trying to decode or re-encode a binary field (e.g., malformed Base64). | The transformation for the specific binary field was skipped, and the original value was used. The system remains operational, but the output JSON may not have the expected encoding for that field. |
| "backend sent xsi:nil for non-nillable field" (log) | The SOAP response contained an element with xsi:nil="true" for a field that was marked as non-nillable in the contract. | This indicates a discrepancy between the contract and the backend's behavior. The mapper will still produce a JSON null to reflect the backend's intent. |
RuntimeController: The typical caller of SoapMessageMapper.ContractPatternValidator: A component used for validating string values against regex patterns from XSDs.EncodingRegistry: Manages custom field encoding/decoding strategies.