This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.22.x by this push:
new 36f8ecacce88 [backport camel-4.22.x] CAMEL-26585: camel-aws2-sns -
serialize array attributes as valid JSON (#29182)
36f8ecacce88 is described below
commit 36f8ecacce882e54f0787c8f771369412d25902e
Author: Guillaume Nodet <[email protected]>
AuthorDate: Wed Nov 4 20:07:46 2026 +0100
[backport camel-4.22.x] CAMEL-26585: camel-aws2-sns - serialize array
attributes as valid JSON (#29182)
---
.../camel/component/aws2/sns/Sns2Producer.java | 13 +++++-
.../camel/component/aws2/sns/SnsProducerTest.java | 50 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_22.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_22.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index 4ffee56c75ca..3afd44e3029f 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -1600,6 +1600,17 @@ or when the queue is FIFO (AWS rejects per-message delay
with `InvalidParameterV
and was redundant on delay queues. If your route relied on per-message delay
overrides on a delay queue,
the override will now be silently ignored.
+=== 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-xslt-saxon - secure processing now applied unconditionally
The `secureProcessing` option (default `true`) is now applied to the Saxon
`TransformerFactory`