HoistField.java
Overview
The `HoistField` class is a simple, focused utility within the Apache Camel Kafka component's message transformation utilities. Its primary purpose is to **wrap (or "hoist") the existing message body inside a new JSON object under a specified field name**.
This transformation is commonly used when you need to nest the entire current message payload inside a new JSON structure, for example, to comply with a schema, prepare the message for downstream processing, or add contextual grouping around the existing data.
The class leverages Jackson's `ObjectMapper` and `JsonNode` for JSON processing and integrates with Apache Camel's `Exchange` abstraction, making it suitable for use as a processor or transformation step in Camel routes handling Kafka messages.
Class: HoistField
Package
org.apache.camel.component.kafka.transform
Description
`HoistField` provides a method `process` that takes a field name (from an exchange property) and the Camel `Exchange` object. It produces a new JSON object where the original message body is set as the value of the specified field, effectively nesting the original content one level deeper.
Method Summary
Method Signature | Description |
|---|---|
`public JsonNode process(@ExchangeProperty("field") String field, Exchange ex) throws InvalidPayloadException` | Wraps the current message body inside a new JSON object under the given `field` name and returns it as a `JsonNode`. |
Detailed Method Explanation
process
public JsonNode process(@ExchangeProperty("field") String field, Exchange ex) throws InvalidPayloadException
Parameters:
@ExchangeProperty("field") String field
The name of the field under which the existing message body should be nested. This value is retrieved from the Camel Exchange property named"field".Exchange ex
The Apache Camel Exchange object representing the current message exchange, providing access to the message body and headers.
Returns:
AJsonNoderepresenting a new JSON object where the original message body is nested under the providedfield.Throws:
InvalidPayloadExceptionif the message body is not valid or cannot be processed.Behavior:
Creates an instance of Jackson's
ObjectMapper.Retrieves the current message body as-is (without explicit conversion).
Constructs a new
Map<Object,Object>with a single entry where the key is the providedfieldand the value is the original body.Converts this map to a Jackson
JsonNodeusingvalueToTree, which serializes the map into a JSON structure.Returns the resulting
JsonNodefor downstream usage.
Usage Example:
// Assuming this is called within a Camel route processor or bean
HoistField hoistField = new HoistField();
// Set the field name in the exchange property (usually done by Camel route or processor configuration)
exchange.setProperty("field", "payload");
// Process to hoist the body
JsonNode hoistedJson = hoistField.process(exchange.getProperty("field", String.class), exchange);
// Optionally set the new body on the message
exchange.getMessage().setBody(hoistedJson);
Example Input Message Body:
{
"id": 123,
"name": "Alice"
}
Exchange Property:
field = "user"
Resulting JSON (returned
JsonNode):
{
"user": {
"id": 123,
"name": "Alice"
}
}
Important Implementation Details
Jackson ObjectMapper Usage:
The method usesObjectMapper.valueToTree(Map)to convert a Java Map into a JacksonJsonNode. This is an efficient way to produce a JSON tree node without manual serialization.No Deep Parsing of Original Body:
The original body is inserted as-is as the value in the map. If the body is already a JSON-compatible object (e.g., a JacksonJsonNodeor a POJO serializable by Jackson), it will be nested properly. If not, the result depends on Jackson's ability to serialize the object.Exchange Property Dependency:
The name of the hoisting field is dynamically supplied via the exchange property"field". This design allows flexible routing-time configuration without hardcoding the field name.Exception Handling:
The method declaresInvalidPayloadExceptionbut does not explicitly throw it in the current implementation. This exception can be thrown if the message body is invalid or incompatible in broader usage contexts.
Interaction with Other System Components
Apache Camel Exchange:
The class operates on the CamelExchange, accessing the message body and properties. It expects the"field"property to be set before invocation.Kafka Message Transformation Pipeline:
HoistFieldis typically part of a chain of JSON transformation processors in a Kafka message flow within Apache Camel. It prepares messages by structuring payloads for downstream consumers or further processing.Jackson JSON Processing:
Uses Jackson's tree model (JsonNode) for output, enabling seamless integration with other processors that consume or manipulate JSON structures.Integration Example:
In a Camel route, a user might include theHoistFieldprocessor after other transformations to package the message body inside a named JSON field before sending to Kafka.
Visual Diagram
classDiagram
class HoistField {
+JsonNode process(String field, Exchange ex) throws InvalidPayloadException
}
HoistField ..> ObjectMapper : uses
HoistField ..> Exchange : accesses
Summary
The `HoistField` class encapsulates a straightforward but often-needed JSON transformation: wrapping an existing message body into a new JSON object under a specified field name. It provides flexibility by accepting the target field name dynamically from the Camel exchange properties and producing a Jackson `JsonNode` output ready for further processing or direct message body replacement.
This utility fits within the broader **Message Transformation Utilities** in the Apache Camel Kafka component, enabling clean and maintainable JSON payload restructuring in Kafka messaging workflows.
Appendix: Usage in Apache Camel Route (Example)
from("kafka:input-topic")
.process(exchange -> {
// Set the field name property dynamically (could be static or expression-based)
exchange.setProperty("field", "wrappedPayload");
})
.process(new HoistField())
.marshal().json() // Convert JsonNode to JSON string
.to("kafka:output-topic");
This route snippet wraps incoming messages under `"wrappedPayload"` before forwarding them.