This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new b77a58944178 CAMEL-25360: camel-cloudevents - write the data of 
application-cloudevents+json as its datacontenttype says (#27587)
b77a58944178 is described below

commit b77a5894417813bf0bf98af97af110873b103535
Author: allthingssecurity <[email protected]>
AuthorDate: Sat Oct 10 01:29:08 2026 +0530

    CAMEL-25360: camel-cloudevents - write the data of 
application-cloudevents+json as its datacontenttype says (#27587)
    
    The application-cloudevents+json data type nested any JSON object or array 
body
    whatever the datacontenttype, wrote JSON scalars as strings under a JSON
    content type, and decoded byte[] bodies as text. The data is now written as 
the
    CloudEvents JSON format describes, using the event's datacontenttype: JSON
    values are nested for a JSON media type, bytes go to data_base64, and a null
    body gives an event without data. Upgrade guide note added.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../CloudEventJsonDataTypeTransformer.java         | 122 +++++++++++++-
 .../CloudEventJsonDataTypeTransformerTest.java     | 177 +++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    |  15 ++
 3 files changed, 306 insertions(+), 8 deletions(-)

diff --git 
a/components/camel-cloudevents/src/main/java/org/apache/camel/component/cloudevents/transformer/CloudEventJsonDataTypeTransformer.java
 
b/components/camel-cloudevents/src/main/java/org/apache/camel/component/cloudevents/transformer/CloudEventJsonDataTypeTransformer.java
index cc7be5efbc93..e3eec532d1db 100644
--- 
a/components/camel-cloudevents/src/main/java/org/apache/camel/component/cloudevents/transformer/CloudEventJsonDataTypeTransformer.java
+++ 
b/components/camel-cloudevents/src/main/java/org/apache/camel/component/cloudevents/transformer/CloudEventJsonDataTypeTransformer.java
@@ -17,8 +17,14 @@
 
 package org.apache.camel.component.cloudevents.transformer;
 
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CodingErrorAction;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
 import org.apache.camel.Exchange;
@@ -28,12 +34,25 @@ import org.apache.camel.cloudevents.CloudEvents;
 import org.apache.camel.spi.DataType;
 import org.apache.camel.spi.DataTypeTransformer;
 import org.apache.camel.spi.Transformer;
+import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.MessageHelper;
 
 /**
  * Data type represents a default Camel CloudEvent V1 Json format binding. The 
data type reads Camel specific CloudEvent
  * headers and transforms these to a Json object representing the CloudEvents 
Json format specification. Sets default
  * values for CloudEvent attributes such as the Http content type header, 
event source, event type.
+ * <p/>
+ * The body is written as the data of the event according to its 
datacontenttype (CloudEvents Json format, section 3.1):
+ * <ul>
+ * <li>declared Json content type (subtype {@code json} or suffix {@code 
+json}): a body that is a Json value, scalars
+ * included, is nested as that value; any other body is a Json string</li>
+ * <li>no declared content type ({@code application/json} is assumed): only a 
Json object or array is nested, so that
+ * text such as a long number or {@code null} stays a string</li>
+ * <li>any other content type: the body is a Json string</li>
+ * <li>a {@code byte[]} body that is not valid text in the charset of the 
exchange: {@code data_base64}, with the
+ * datacontenttype {@code application/octet-stream} unless one was 
declared</li>
+ * <li>a null body: no data</li>
+ * </ul>
  */
 @DataTypeTransformer(name = "application-cloudevents+json",
                      description = "Adds default CloudEvent (JSon binding) 
headers to the Camel message (such as content-type, event source, event type 
etc.)")
@@ -55,6 +74,9 @@ public class CloudEventJsonDataTypeTransformer extends 
Transformer {
         final Map<String, Object> headers = message.getHeaders();
 
         String dataContentType = 
headers.getOrDefault(CloudEvent.CAMEL_CLOUD_EVENT_CONTENT_TYPE, 
APPLICATION_JSON).toString();
+        // whether the content type of the data was declared, rather than 
assumed to be application/json
+        boolean contentTypeDeclared = 
headers.get(CloudEvent.CAMEL_CLOUD_EVENT_DATA_CONTENT_TYPE) != null
+                || headers.get(CloudEvent.CAMEL_CLOUD_EVENT_CONTENT_TYPE) != 
null;
         if (!APPLICATION_CLOUDEVENTS_JSON.equals(dataContentType)) {
             Map<String, Object> cloudEventAttributes = new HashMap<>();
             CloudEvent cloudEvent = CloudEvents.v1_0;
@@ -76,28 +98,52 @@ public class CloudEventJsonDataTypeTransformer extends 
Transformer {
             
cloudEventAttributes.putIfAbsent(cloudEvent.mandatoryAttribute(CloudEvent.CAMEL_CLOUD_EVENT_TIME).json(),
                     cloudEvent.getEventTime(message.getExchange()));
 
-            String body = MessageHelper.extractBodyAsString(message);
-            cloudEventAttributes.putIfAbsent("data", body);
-            cloudEventAttributes.putIfAbsent(
-                    
cloudEvent.mandatoryAttribute(CloudEvent.CAMEL_CLOUD_EVENT_DATA_CONTENT_TYPE).json(),
 dataContentType);
+            String dataContentTypeKey = 
cloudEvent.mandatoryAttribute(CloudEvent.CAMEL_CLOUD_EVENT_DATA_CONTENT_TYPE).json();
+            cloudEventAttributes.putIfAbsent(dataContentTypeKey, 
dataContentType);
+            // the data is written as the datacontenttype of the event says
+            Object eventDataContentType = 
cloudEventAttributes.get(dataContentTypeKey);
+            boolean jsonData = isJsonContentType(eventDataContentType != null 
? eventDataContentType.toString() : null);
+            if (message.getBody() instanceof byte[] bytes) {
+                String text = decodeText(bytes, 
ExchangeHelper.getCharset(message.getExchange()));
+                if (text != null) {
+                    cloudEventAttributes.putIfAbsent("data", text);
+                } else {
+                    // decoding these bytes as text would replace the invalid 
ones, so the data would be lost
+                    cloudEventAttributes.putIfAbsent("data_base64", 
Base64.getEncoder().encodeToString(bytes));
+                    // a declared datacontenttype is kept: the route states 
the format of these bytes, which may be valid
+                    // in a charset other than the one of the exchange, so 
only an assumed application/json is replaced
+                    if (!contentTypeDeclared) {
+                        // these bytes are not Json, and the datacontenttype 
must reflect the format of the data
+                        cloudEventAttributes.put(dataContentTypeKey, 
CloudEvent.APPLICATION_OCTET_STREAM_MIME_TYPE);
+                    }
+                }
+            } else {
+                String data = MessageHelper.extractBodyAsString(message);
+                // the data is optional, so a null body has none rather than 
the text "null"
+                if (data != null) {
+                    cloudEventAttributes.putIfAbsent("data", data);
+                }
+            }
 
             headers.put(Exchange.CONTENT_TYPE, APPLICATION_CLOUDEVENTS_JSON);
 
-            message.setBody(createCouldEventJsonObject(cloudEventAttributes));
+            message.setBody(createCloudEventJsonObject(cloudEventAttributes, 
jsonData, contentTypeDeclared));
 
             
cloudEvent.attributes().stream().map(CloudEvent.Attribute::id).forEach(headers::remove);
         }
     }
 
-    private String createCouldEventJsonObject(Map<String, Object> 
cloudEventAttributes) {
+    private String createCloudEventJsonObject(
+            Map<String, Object> cloudEventAttributes, boolean jsonData, 
boolean contentTypeDeclared) {
         StringBuilder builder = new StringBuilder("{");
 
         cloudEventAttributes.forEach((key, value) -> {
             builder.append(" ");
             appendJsonString(builder, key);
             builder.append(":");
-            if ("data".equals(key) && value instanceof String data && 
isJson(data)) {
-                // set Json data as nested object in the data field
+            if (jsonData && "data".equals(key) && value instanceof String data
+                    && (contentTypeDeclared ? isJsonValue(data) : 
isJson(data))) {
+                // set Json data as nested value in the data field, a scalar 
only if Json was declared
                 builder.append(data);
             } else {
                 appendJsonString(builder, String.valueOf(value));
@@ -140,6 +186,66 @@ public class CloudEventJsonDataTypeTransformer extends 
Transformer {
         builder.append('"');
     }
 
+    /**
+     * Whether the content type declares Json data: the subtype of its media 
type, without parameters, is {@code json}
+     * or ends with {@code +json} (CloudEvents Json format, section 3.1). An 
absent content type is Json, as the
+     * transformer declares {@code application/json} then.
+     */
+    static boolean isJsonContentType(String contentType) {
+        if (contentType == null) {
+            return true;
+        }
+        String subtype = mediaSubtype(contentType);
+        return subtype != null && (subtype.equals("json") || 
subtype.endsWith("+json"));
+    }
+
+    /**
+     * The bytes decoded as text in the given charset, or {@code null} if they 
are not valid text in it.
+     */
+    static String decodeText(byte[] data, Charset charset) {
+        try {
+            return charset.newDecoder()
+                    .onMalformedInput(CodingErrorAction.REPORT)
+                    .onUnmappableCharacter(CodingErrorAction.REPORT)
+                    .decode(ByteBuffer.wrap(data))
+                    .toString();
+        } catch (CharacterCodingException e) {
+            return null;
+        }
+    }
+
+    private static String mediaType(String contentType) {
+        int semicolon = contentType.indexOf(';');
+        return (semicolon < 0 ? contentType : contentType.substring(0, 
semicolon)).trim().toLowerCase(Locale.ROOT);
+    }
+
+    private static String mediaSubtype(String contentType) {
+        String mediaType = mediaType(contentType);
+        int slash = mediaType.indexOf('/');
+        return slash < 0 ? null : mediaType.substring(slash + 1);
+    }
+
+    /**
+     * Whether the data is a Json value: an object, an array, a string, a 
number, true, false or null.
+     */
+    static boolean isJsonValue(String data) {
+        if (data == null) {
+            return false;
+        }
+        final int length = data.length();
+        int i = skipWhitespace(data, 0, length);
+        if (i == length) {
+            return false;
+        }
+        char ch = data.charAt(i);
+        if (ch == '{' || ch == '[') {
+            return isJson(data);
+        }
+        i = skipScalar(data, i, length);
+        // nothing but whitespace may follow the value
+        return i >= 0 && skipWhitespace(data, i, length) == length;
+    }
+
     /**
      * Whether the data is a Json object or array, which is then set as nested 
Json value. Text that only starts like
      * Json (such as a log line "[INFO] ...") is set as a Json string.
diff --git 
a/components/camel-cloudevents/src/test/java/org/apache/camel/component/cloudevents/transformer/CloudEventJsonDataTypeTransformerTest.java
 
b/components/camel-cloudevents/src/test/java/org/apache/camel/component/cloudevents/transformer/CloudEventJsonDataTypeTransformerTest.java
index f9ba5294e240..ad8fe6a458e5 100644
--- 
a/components/camel-cloudevents/src/test/java/org/apache/camel/component/cloudevents/transformer/CloudEventJsonDataTypeTransformerTest.java
+++ 
b/components/camel-cloudevents/src/test/java/org/apache/camel/component/cloudevents/transformer/CloudEventJsonDataTypeTransformerTest.java
@@ -18,7 +18,10 @@
 package org.apache.camel.component.cloudevents.transformer;
 
 import java.io.ByteArrayInputStream;
+import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Collection;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.cloudevents.CloudEvent;
@@ -33,8 +36,10 @@ import org.apache.camel.util.json.Jsoner;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -214,6 +219,178 @@ class CloudEventJsonDataTypeTransformerTest {
         assertEquals(text, event.getString("data"));
     }
 
+    private JsonObject transformToEvent(String contentType, Object body) 
throws Exception {
+        Exchange exchange = new DefaultExchange(camelContext);
+        if (contentType != null) {
+            
exchange.getMessage().setHeader(CloudEvent.CAMEL_CLOUD_EVENT_CONTENT_TYPE, 
contentType);
+        }
+        exchange.getMessage().setBody(body);
+
+        transformer.transform(exchange.getMessage(), DataType.ANY, 
DataType.ANY);
+
+        return (JsonObject) 
Jsoner.deserialize(exchange.getMessage().getBody(String.class));
+    }
+
+    @Test
+    void shouldNotNestJsonDataOfANonJsonContentType() throws Exception {
+        // CloudEvents JSON format 3.1: only data whose datacontenttype is 
JSON is a JSON value, other text is a string
+        assertEquals("[1,2]", transformToEvent("text/plain", 
"[1,2]").get("data"));
+        assertEquals("{\"a\": 1}", transformToEvent("application/xml", 
"{\"a\": 1}").get("data"));
+    }
+
+    @Test
+    void shouldUseTheDataContentTypeOfTheEvent() throws Exception {
+        Exchange exchange = new DefaultExchange(camelContext);
+        // the datacontenttype attribute of the event wins over the 
Content-Type of the message
+        
exchange.getMessage().setHeader(CloudEvent.CAMEL_CLOUD_EVENT_DATA_CONTENT_TYPE, 
"text/plain");
+        
exchange.getMessage().setHeader(CloudEvent.CAMEL_CLOUD_EVENT_CONTENT_TYPE, 
"application/json");
+        exchange.getMessage().setBody("[1,2]");
+
+        transformer.transform(exchange.getMessage(), DataType.ANY, 
DataType.ANY);
+
+        JsonObject event = (JsonObject) 
Jsoner.deserialize(exchange.getMessage().getBody(String.class));
+        assertEquals("text/plain", event.getString(
+                
CloudEvents.v1_0.mandatoryAttribute(CloudEvent.CAMEL_CLOUD_EVENT_DATA_CONTENT_TYPE).json()));
+        assertEquals("[1,2]", event.get("data"));
+    }
+
+    @Test
+    void shouldNestJsonScalarsOfAJsonContentType() throws Exception {
+        assertEquals(new BigDecimal("42"), 
transformToEvent("application/json", "42").get("data"));
+        assertEquals(new BigDecimal("-1.5E+3"), 
transformToEvent("application/json", " -1.5e3 ").get("data"));
+        assertEquals(Boolean.TRUE, transformToEvent("application/json", 
"true").get("data"));
+        assertEquals("text", transformToEvent("application/json", 
"\"text\"").get("data"));
+        JsonObject event = transformToEvent("application/json", "null");
+        assertTrue(event.containsKey("data"));
+        assertNull(event.get("data"));
+        assertEquals(Boolean.FALSE, 
transformToEvent("application/vnd.example+json; charset=UTF-8", 
"false").get("data"));
+    }
+
+    @Test
+    void shouldNestJsonScalarsOfAJsonDataContentType() throws Exception {
+        Exchange exchange = new DefaultExchange(camelContext);
+        
exchange.getMessage().setHeader(CloudEvent.CAMEL_CLOUD_EVENT_DATA_CONTENT_TYPE, 
"application/json");
+        exchange.getMessage().setBody("42");
+
+        transformer.transform(exchange.getMessage(), DataType.ANY, 
DataType.ANY);
+
+        JsonObject event = (JsonObject) 
Jsoner.deserialize(exchange.getMessage().getBody(String.class));
+        assertEquals(new BigDecimal("42"), event.get("data"));
+    }
+
+    @Test
+    void shouldNestOnlyJsonObjectsAndArraysWithoutADeclaredContentType() 
throws Exception {
+        // the transformer only assumes application/json, so scalars stay 
strings as before (CAMEL-22339)
+        for (String text : new String[] { "42", "1234567890123456789", 
"-1.5e3", "true", "null", "\"text\"" }) {
+            JsonObject event = transformToEvent(null, text);
+            assertEquals(text, event.get("data"), text);
+            assertEquals("application/json", event.getString(
+                    
CloudEvents.v1_0.mandatoryAttribute(CloudEvent.CAMEL_CLOUD_EVENT_DATA_CONTENT_TYPE).json()),
 text);
+        }
+        assertEquals("42", transformToEvent(null, 
"42".getBytes(StandardCharsets.UTF_8)).get("data"));
+        JsonObject data = (JsonObject) transformToEvent(null, "{\"a\": 
1}").get("data");
+        assertEquals(1, data.getInteger("a"));
+        assertEquals(2, ((Collection<?>) transformToEvent(null, "[1, 
2]").get("data")).size());
+    }
+
+    @Test
+    void shouldWriteNoDataForANullBody() throws Exception {
+        for (String contentType : new String[] { "application/json", 
"text/plain", null }) {
+            JsonObject event = transformToEvent(contentType, null);
+
+            assertFalse(event.containsKey("data"), contentType);
+            assertFalse(event.containsKey("data_base64"), contentType);
+        }
+    }
+
+    @Test
+    void shouldNestJsonOfAnyJsonContentType() throws Exception {
+        JsonObject data = (JsonObject) 
transformToEvent("application/vnd.example+json; charset=UTF-8", "{\"a\": 1}")
+                .get("data");
+        assertEquals(1, data.getInteger("a"));
+        data = (JsonObject) transformToEvent("Application/JSON", "{\"a\": 
1}").get("data");
+        assertEquals(1, data.getInteger("a"));
+    }
+
+    @Test
+    void shouldKeepTextOfAJsonContentTypeThatIsNotJsonAsString() throws 
Exception {
+        for (String text : new String[] {
+                "", "  ", "Test", "42 apples", "4 2", "trueish", "nul", "01", 
"+1", "\"open", "\"a\\x\"", "\"a\" \"b\"",
+                "\"raw\u0001control\"" }) {
+            assertEquals(text, transformToEvent("application/json", 
text).get("data"), text);
+        }
+        assertEquals("42 apples", transformToEvent(null, "42 
apples").get("data"));
+    }
+
+    @Test
+    void shouldWriteBinaryDataAsBase64() throws Exception {
+        byte[] bytes = { 0, 1, (byte) 0x80, (byte) 0xff, '"', '\\' };
+
+        JsonObject event = transformToEvent("application/octet-stream", bytes);
+
+        assertEquals(Base64.getEncoder().encodeToString(bytes), 
event.get("data_base64"));
+        assertFalse(event.containsKey("data"));
+        assertEquals("application/octet-stream", event.getString(
+                
CloudEvents.v1_0.mandatoryAttribute(CloudEvent.CAMEL_CLOUD_EVENT_DATA_CONTENT_TYPE).json()));
+    }
+
+    @Test
+    void shouldWriteBytesThatAreNotValidTextAsBase64WhateverTheContentType() 
throws Exception {
+        byte[] bytes = { 'a', (byte) 0xc3, '(' };
+
+        for (String contentType : new String[] { "text/plain", 
"application/json", null }) {
+            JsonObject event = transformToEvent(contentType, bytes);
+
+            assertEquals(Base64.getEncoder().encodeToString(bytes), 
event.get("data_base64"), contentType);
+            assertFalse(event.containsKey("data"), contentType);
+            // a declared content type is kept, the default application/json 
would not reflect these bytes
+            assertEquals(contentType != null ? contentType : 
CloudEvent.APPLICATION_OCTET_STREAM_MIME_TYPE,
+                    event.getString(
+                            
CloudEvents.v1_0.mandatoryAttribute(CloudEvent.CAMEL_CLOUD_EVENT_DATA_CONTENT_TYPE).json()),
+                    contentType);
+        }
+    }
+
+    @Test
+    void shouldWriteBytesThatAreValidTextAsStringWhenTheContentTypeIsBinary() 
throws Exception {
+        // valid UTF-8 is written without loss as a Json string, as before: a 
reader gets the same bytes back
+        byte[] bytes = { 0, 1, '"', '\\', 'a' };
+        Object data = transformToEvent("application/octet-stream", 
bytes).get("data");
+        assertArrayEquals(bytes, assertInstanceOf(String.class, 
data).getBytes(StandardCharsets.UTF_8));
+        assertEquals("Grüße", transformToEvent("application/octet-stream", 
"Grüße".getBytes(StandardCharsets.UTF_8))
+                .get("data"));
+        // a Json body of a content type that is not Json is a string, not 
nested
+        JsonObject event = transformToEvent("application/octet-stream", 
"{\"a\": 1}".getBytes(StandardCharsets.UTF_8));
+        assertEquals("{\"a\": 1}", event.get("data"));
+        assertFalse(event.containsKey("data_base64"));
+    }
+
+    @Test
+    void shouldDecodeBytesInTheCharsetOfTheExchange() throws Exception {
+        Exchange exchange = new DefaultExchange(camelContext);
+        exchange.setProperty(Exchange.CHARSET_NAME, "ISO-8859-1");
+        
exchange.getMessage().setHeader(CloudEvent.CAMEL_CLOUD_EVENT_CONTENT_TYPE, 
"application/octet-stream");
+        exchange.getMessage().setBody(new byte[] { 'c', 'a', 'f', (byte) 0xe9 
});
+
+        transformer.transform(exchange.getMessage(), DataType.ANY, 
DataType.ANY);
+
+        JsonObject event = (JsonObject) 
Jsoner.deserialize(exchange.getMessage().getBody(String.class));
+        assertEquals("caf\u00e9", event.get("data"));
+    }
+
+    @Test
+    void shouldWriteBytesOfTextOrJsonContentTypesAsData() throws Exception {
+        byte[] text = "Grüße".getBytes(StandardCharsets.UTF_8);
+        assertEquals("Grüße", transformToEvent("text/plain", 
text).get("data"));
+        assertEquals("<a>Grüße</a>", transformToEvent("application/xml", 
"<a>Grüße</a>".getBytes(StandardCharsets.UTF_8))
+                .get("data"));
+        JsonObject data = (JsonObject) transformToEvent("application/json", 
"{\"a\": 1}".getBytes(StandardCharsets.UTF_8))
+                .get("data");
+        assertEquals(1, data.getInteger("a"));
+        // without a datacontenttype the data is Json, so bytes are read as 
text as before
+        assertEquals("Test", transformToEvent(null, 
"Test".getBytes(StandardCharsets.UTF_8)).get("data"));
+    }
+
     @Test
     void shouldRecognizeJsonObjectsAndArrays() {
         assertTrue(CloudEventJsonDataTypeTransformer.isJson("{}"));
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index fa318f661406..003caaa164bb 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -645,6 +645,21 @@ sent to the partition with the same id in the target Event 
Hub. To keep doing so
 Event Hubs that have the same partitions, remove the 
`CamelAzureEventHubsReceivedPartitionId` exchange property before
 the producer.
 
+=== camel-cloudevents - data of the application-cloudevents+json data type 
(Breaking change)
+
+The `application-cloudevents+json` data type now writes the body according to 
the `datacontenttype` of the event
+(CloudEvents JSON format, section 3.1): with a declared JSON content type (a 
`Content-Type` or
+`CamelCloudEventDataContentType` header with a `json` subtype or `+json` 
suffix) a JSON scalar body is now nested too
+(`"data":42` instead of `"data":"42"`), while without a declared content type 
only a JSON object or array is nested, as
+before; with any other content type a JSON object or array body is now written 
as a string; a `byte[]` body that is not
+valid text in the exchange charset is written as `data_base64`, with the 
`datacontenttype` `application/octet-stream`
+when no content type was declared; and a null body gives an event without 
`data` instead of `"data":"null"`.
+Only a `byte[]` body is kept as bytes: a body of any other type, such as an 
`InputStream`, is still read as text in
+the exchange charset, as before, so bytes that are not valid text are still 
replaced.
+Component CloudEvent data types that declare `application/octet-stream` or 
`text/plain` (such as `aws2-s3`,
+`aws-cloudtrail` or `google-pubsub`) therefore no longer nest a JSON body: set 
the `CamelCloudEventDataContentType`
+header to `application/json` before `application-cloudevents+json` to keep it 
nested.
+
 === camel-consul - readTimeout and writeTimeout
 
 The `readTimeout` and `writeTimeout` options (also on the Consul cluster 
service) set the connect timeout of the

Reply via email to