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 409b8b7cc43c CAMEL-26585: camel-aws2-sns - serialize array attributes
as valid JSON (#29158)
409b8b7cc43c is described below
commit 409b8b7cc43ce8858a378d48ab8d9b053fa10bef
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Nov 4 16:26:50 2026 +0100
CAMEL-26585: camel-aws2-sns - serialize array attributes as valid JSON
(#29158)
Co-authored-by: OpenAI Codex <[email protected]>
---
.../camel/component/aws2/sns/Sns2Producer.java | 13 +++++-
.../camel/component/aws2/sns/SnsProducerTest.java | 50 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 11 +++++
3 files changed, 73 insertions(+), 1 deletion(-)
diff --git
a/components/camel-aws/camel-aws2-sns/src/main/java/org/apache/camel/component/aws2/sns/Sns2Producer.java
b/components/camel-aws/camel-aws2-sns/src/main/java/org/apache/camel/component/aws2/sns/Sns2Producer.java
index ed7a28661363..cdc8035fe2a4 100644
---
a/components/camel-aws/camel-aws2-sns/src/main/java/org/apache/camel/component/aws2/sns/Sns2Producer.java
+++
b/components/camel-aws/camel-aws2-sns/src/main/java/org/apache/camel/component/aws2/sns/Sns2Producer.java
@@ -35,6 +35,7 @@ import org.apache.camel.spi.HeaderFilterStrategy;
import org.apache.camel.support.DefaultProducer;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.URISupport;
+import org.apache.camel.util.json.Jsoner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.SdkBytes;
@@ -153,7 +154,7 @@ public class Sns2Producer extends DefaultProducer {
result.put(entry.getKey(), mav.build());
} else if (value instanceof List) {
String resultString = ((List<?>) value).stream()
- .map(o -> o instanceof String ?
String.format("\"%s\"", o) : Objects.toString(o))
+ .map(Sns2Producer::toStringArrayValue)
.collect(Collectors.joining(", "));
MessageAttributeValue.Builder mav =
MessageAttributeValue.builder();
mav.dataType("String.Array");
@@ -170,6 +171,16 @@ public class Sns2Producer extends DefaultProducer {
return result;
}
+ private static String toStringArrayValue(Object value) {
+ boolean jsonPrimitive = value == null || value instanceof Boolean ||
value instanceof Number;
+ if (value instanceof Double number) {
+ jsonPrimitive = Double.isFinite(number);
+ } else if (value instanceof Float number) {
+ jsonPrimitive = Float.isFinite(number);
+ }
+ return jsonPrimitive ? Objects.toString(value) : "\"" +
Jsoner.escape(value.toString()) + "\"";
+ }
+
private void configureFifoAttributes(PublishRequest.Builder request,
Exchange exchange) {
if (getEndpoint().getConfiguration().isFifoTopic()) {
// use strategies
diff --git
a/components/camel-aws/camel-aws2-sns/src/test/java/org/apache/camel/component/aws2/sns/SnsProducerTest.java
b/components/camel-aws/camel-aws2-sns/src/test/java/org/apache/camel/component/aws2/sns/SnsProducerTest.java
index a780c490cb2d..f9154b68c223 100644
---
a/components/camel-aws/camel-aws2-sns/src/test/java/org/apache/camel/component/aws2/sns/SnsProducerTest.java
+++
b/components/camel-aws/camel-aws2-sns/src/test/java/org/apache/camel/component/aws2/sns/SnsProducerTest.java
@@ -17,10 +17,14 @@
package org.apache.camel.component.aws2.sns;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.UUID;
import org.apache.camel.Exchange;
+import org.apache.camel.util.json.Jsoner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -48,6 +52,52 @@ public class SnsProducerTest {
when(endpoint.getHeaderFilterStrategy()).thenReturn(new
Sns2HeaderFilterStrategy());
}
+ @Test
+ public void translateStringArrayEscapesJsonCharacters() throws Exception {
+ List<String> values = List.of("a\"b", "C:\\temp", "line\nbreak",
"tab\tvalue", "\b\f\r\u0001", "caf\u00e9",
+ "application/json", "\u007f", "\u2000");
+
+ MessageAttributeValue attribute =
producer.translateAttributes(Map.of("values", values), exchange).get("values");
+
+ assertThat(attribute.dataType(), is("String.Array"));
+ assertThat(Jsoner.deserialize(attribute.stringValue()), is(values));
+ }
+
+ @Test
+ public void translateStringArrayQuotesOtherMemberTypes() throws Exception {
+ UUID id = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ Date date = new Date(0);
+ List<Object> values = List.of(Membership.GOLD, 'x', id, date);
+
+ MessageAttributeValue attribute =
producer.translateAttributes(Map.of("values", values), exchange).get("values");
+
+ assertThat(Jsoner.deserialize(attribute.stringValue()),
is(List.of("GOLD", "x", id.toString(), date.toString())));
+ }
+
+ @Test
+ public void translateStringArrayQuotesNonFiniteNumbers() throws Exception {
+ List<Number> values = List.of(Double.NaN, Double.POSITIVE_INFINITY,
Double.NEGATIVE_INFINITY,
+ Float.NaN, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY);
+
+ MessageAttributeValue attribute =
producer.translateAttributes(Map.of("values", values), exchange).get("values");
+
+ assertThat(Jsoner.deserialize(attribute.stringValue()),
+ is(List.of("NaN", "Infinity", "-Infinity", "NaN", "Infinity",
"-Infinity")));
+ }
+
+ @Test
+ public void translateStringArrayKeepsJsonPrimitives() {
+ List<Object> values = Arrays.asList(12, 1.5D, 2.5F, true, null);
+
+ MessageAttributeValue attribute =
producer.translateAttributes(Map.of("values", values), exchange).get("values");
+
+ assertThat(attribute.stringValue(), is("[12, 1.5, 2.5, true, null]"));
+ }
+
+ private enum Membership {
+ GOLD
+ }
+
@Test
public void translateAttributes() {
Map<String, Object> headers = new HashMap<>();
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 5e6b2f2bbbeb..e4b731389b10 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -5606,6 +5606,17 @@ The `splitResult` option of the google-sheets consumer
now defaults to `true`, a
components do: an array or collection result gives one exchange per element.
Set `splitResult=false` for one exchange
with the whole result.
+=== camel-aws2-sns - string array message attributes
+
+String values in list-valued message headers are now JSON-escaped when mapped
to SNS
+`String.Array` message attributes. Quotes, backslashes and control characters
are preserved
+as string data. Routes should supply the original strings rather than
pre-escaping them.
+Other member types, such as enums, characters, UUIDs and dates, are serialized
as JSON strings.
+Non-finite floating-point values (`NaN` and infinities) are also serialized as
strings; finite numbers,
+booleans and null remain JSON primitives.
+The JSON utility also escapes forward slashes and some Unicode characters, so
raw `String.Array`
+attribute text may differ even when its decoded string values are unchanged.
+
=== camel-aws2-sqs - sendBatchMessage of more than 10 entries
With more than 10 entries, which are sent in requests of 10, the message body
is now one `SendMessageBatchResponse`