Skip to content

Commit 55fddb3

Browse files
authored
feat: JSON format to assume JSON content-type where possible (#604)
Signed-off-by: Matej Vašek <mvasek@redhat.com>
1 parent 7b9d020 commit 55fddb3

File tree

4 files changed

+98
-8
lines changed

4 files changed

+98
-8
lines changed

formats/json-jackson/src/main/java/io/cloudevents/jackson/CloudEventDeserializer.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,21 @@
4242
class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
4343
private final boolean forceExtensionNameLowerCaseDeserialization;
4444
private final boolean forceIgnoreInvalidExtensionNameDeserialization;
45+
private final boolean disableDataContentTypeDefaulting;
4546

4647
protected CloudEventDeserializer() {
47-
this(false, false);
48+
this(false, false, false);
4849
}
4950

5051
protected CloudEventDeserializer(
5152
boolean forceExtensionNameLowerCaseDeserialization,
52-
boolean forceIgnoreInvalidExtensionNameDeserialization
53+
boolean forceIgnoreInvalidExtensionNameDeserialization,
54+
boolean disableDataContentTypeDefaulting
5355
) {
5456
super(CloudEvent.class);
5557
this.forceExtensionNameLowerCaseDeserialization = forceExtensionNameLowerCaseDeserialization;
5658
this.forceIgnoreInvalidExtensionNameDeserialization = forceIgnoreInvalidExtensionNameDeserialization;
59+
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
5760
}
5861

5962
private static class JsonMessage implements CloudEventReader {
@@ -62,17 +65,20 @@ private static class JsonMessage implements CloudEventReader {
6265
private final ObjectNode node;
6366
private final boolean forceExtensionNameLowerCaseDeserialization;
6467
private final boolean forceIgnoreInvalidExtensionNameDeserialization;
68+
private final boolean disableDataContentTypeDefaulting;
6569

6670
public JsonMessage(
6771
JsonParser p,
6872
ObjectNode node,
6973
boolean forceExtensionNameLowerCaseDeserialization,
70-
boolean forceIgnoreInvalidExtensionNameDeserialization
74+
boolean forceIgnoreInvalidExtensionNameDeserialization,
75+
boolean disableDataContentTypeDefaulting
7176
) {
7277
this.p = p;
7378
this.node = node;
7479
this.forceExtensionNameLowerCaseDeserialization = forceExtensionNameLowerCaseDeserialization;
7580
this.forceIgnoreInvalidExtensionNameDeserialization = forceIgnoreInvalidExtensionNameDeserialization;
81+
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
7682
}
7783

7884
@Override
@@ -92,6 +98,9 @@ public <T extends CloudEventWriter<V>, V> V read(CloudEventWriterFactory<T, V> w
9298

9399
// Parse datacontenttype if any
94100
String contentType = getOptionalStringNode(this.node, this.p, "datacontenttype");
101+
if (!this.disableDataContentTypeDefaulting && contentType == null && this.node.has("data")) {
102+
contentType = "application/json";
103+
}
95104
if (contentType != null) {
96105
writer.withContextAttribute("datacontenttype", contentType);
97106
}
@@ -257,7 +266,7 @@ public CloudEvent deserialize(JsonParser p, DeserializationContext ctxt) throws
257266
ObjectNode node = ctxt.readValue(p, ObjectNode.class);
258267

259268
try {
260-
return new JsonMessage(p, node, this.forceExtensionNameLowerCaseDeserialization, this.forceIgnoreInvalidExtensionNameDeserialization)
269+
return new JsonMessage(p, node, this.forceExtensionNameLowerCaseDeserialization, this.forceIgnoreInvalidExtensionNameDeserialization, this.disableDataContentTypeDefaulting)
261270
.read(CloudEventBuilder::fromSpecVersion);
262271
} catch (RuntimeException e) {
263272
// Yeah this is bad but it's needed to support checked exceptions...

formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormat.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public static SimpleModule getCloudEventJacksonModule(JsonFormatOptions options)
213213
ceModule.addSerializer(CloudEvent.class, new CloudEventSerializer(
214214
options.isForceDataBase64Serialization(), options.isForceStringSerialization()));
215215
ceModule.addDeserializer(CloudEvent.class, new CloudEventDeserializer(
216-
options.isForceExtensionNameLowerCaseDeserialization(), options.isForceIgnoreInvalidExtensionNameDeserialization()));
216+
options.isForceExtensionNameLowerCaseDeserialization(), options.isForceIgnoreInvalidExtensionNameDeserialization(), options.isDataContentTypeDefaultingDisabled()));
217217
return ceModule;
218218
}
219219

formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormatOptions.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,27 @@ public final class JsonFormatOptions {
2121
private final boolean forceStringSerialization;
2222
private final boolean forceExtensionNameLowerCaseDeserialization;
2323
private final boolean forceIgnoreInvalidExtensionNameDeserialization;
24+
private final boolean disableDataContentTypeDefaulting;
2425

2526
/**
2627
* Create a new instance of this class options the serialization / deserialization.
2728
*/
2829
public JsonFormatOptions() {
29-
this(false, false, false, false);
30+
this(false, false, false, false, false);
3031
}
3132

3233
JsonFormatOptions(
3334
boolean forceDataBase64Serialization,
3435
boolean forceStringSerialization,
3536
boolean forceExtensionNameLowerCaseDeserialization,
36-
boolean forceIgnoreInvalidExtensionNameDeserialization
37+
boolean forceIgnoreInvalidExtensionNameDeserialization,
38+
boolean disableDataContentTypeDefaulting
3739
) {
3840
this.forceDataBase64Serialization = forceDataBase64Serialization;
3941
this.forceStringSerialization = forceStringSerialization;
4042
this.forceExtensionNameLowerCaseDeserialization = forceExtensionNameLowerCaseDeserialization;
4143
this.forceIgnoreInvalidExtensionNameDeserialization = forceIgnoreInvalidExtensionNameDeserialization;
44+
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
4245
}
4346

4447
public static JsonFormatOptionsBuilder builder() {
@@ -61,11 +64,14 @@ public boolean isForceIgnoreInvalidExtensionNameDeserialization() {
6164
return this.forceIgnoreInvalidExtensionNameDeserialization;
6265
}
6366

67+
public boolean isDataContentTypeDefaultingDisabled() { return this.disableDataContentTypeDefaulting; }
68+
6469
public static class JsonFormatOptionsBuilder {
6570
private boolean forceDataBase64Serialization = false;
6671
private boolean forceStringSerialization = false;
6772
private boolean forceExtensionNameLowerCaseDeserialization = false;
6873
private boolean forceIgnoreInvalidExtensionNameDeserialization = false;
74+
private boolean disableDataContentTypeDefaulting = false;
6975

7076
public JsonFormatOptionsBuilder forceDataBase64Serialization(boolean forceDataBase64Serialization) {
7177
this.forceDataBase64Serialization = forceDataBase64Serialization;
@@ -87,12 +93,18 @@ public JsonFormatOptionsBuilder forceIgnoreInvalidExtensionNameDeserialization(b
8793
return this;
8894
}
8995

96+
public JsonFormatOptionsBuilder disableDataContentTypeDefaulting(boolean disableDataContentTypeDefaulting) {
97+
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
98+
return this;
99+
}
100+
90101
public JsonFormatOptions build() {
91102
return new JsonFormatOptions(
92103
this.forceDataBase64Serialization,
93104
this.forceStringSerialization,
94105
this.forceExtensionNameLowerCaseDeserialization,
95-
this.forceIgnoreInvalidExtensionNameDeserialization
106+
this.forceIgnoreInvalidExtensionNameDeserialization,
107+
this.disableDataContentTypeDefaulting
96108
);
97109
}
98110
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package io.cloudevents.jackson;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.databind.module.SimpleModule;
5+
import io.cloudevents.CloudEvent;
6+
import org.junit.jupiter.api.Test;
7+
8+
import java.io.IOException;
9+
import java.io.StringReader;
10+
11+
import static io.cloudevents.jackson.JsonFormat.getCloudEventJacksonModule;
12+
import static org.assertj.core.api.Assertions.assertThat;
13+
14+
public class CloudEventDeserializerTest {
15+
16+
private static final String nonBinaryPayload = "{\n" +
17+
" \"specversion\" : \"1.0\",\n" +
18+
" \"type\" : \"com.example.someevent\",\n" +
19+
" \"source\" : \"/mycontext\",\n" +
20+
" \"subject\": null,\n" +
21+
" \"id\" : \"D234-1234-1234\",\n" +
22+
" \"time\" : \"2018-04-05T17:31:00Z\",\n" +
23+
" \"comexampleextension1\" : \"value\",\n" +
24+
" \"comexampleothervalue\" : 5,\n" +
25+
" \"data\" : \"I'm just a string\"\n" +
26+
"}";
27+
28+
private static final String binaryPayload = "{\n" +
29+
" \"specversion\" : \"1.0\",\n" +
30+
" \"type\" : \"com.example.someevent\",\n" +
31+
" \"source\" : \"/mycontext\",\n" +
32+
" \"id\" : \"D234-1234-1234\",\n" +
33+
" \"data_base64\" : \"eyAieHl6IjogMTIzIH0=\"\n" +
34+
"}";
35+
36+
@Test
37+
void impliedDataContentTypeNonBinaryData() throws IOException {
38+
ObjectMapper mapper = getObjectMapper(false);
39+
StringReader reader = new StringReader(nonBinaryPayload);
40+
CloudEvent ce = mapper.readValue(reader, CloudEvent.class);
41+
assertThat(ce.getDataContentType()).isEqualTo("application/json");
42+
43+
mapper = getObjectMapper(true);
44+
reader = new StringReader(nonBinaryPayload);
45+
ce = mapper.readValue(reader, CloudEvent.class);
46+
assertThat(ce.getDataContentType()).isNull();
47+
}
48+
49+
@Test
50+
void impliedDataContentTypeBinaryData() throws IOException {
51+
final ObjectMapper mapper = getObjectMapper(false);
52+
StringReader reader = new StringReader(binaryPayload);
53+
CloudEvent ce = mapper.readValue(reader, CloudEvent.class);
54+
assertThat(ce.getDataContentType()).isNull();
55+
}
56+
57+
private static ObjectMapper getObjectMapper(boolean disableDataContentTypeDefaulting) {
58+
final ObjectMapper mapper = new ObjectMapper();
59+
final SimpleModule module = getCloudEventJacksonModule(
60+
JsonFormatOptions
61+
.builder()
62+
.disableDataContentTypeDefaulting(disableDataContentTypeDefaulting)
63+
.build()
64+
);
65+
mapper.registerModule(module);
66+
return mapper;
67+
}
68+
69+
}

0 commit comments

Comments
 (0)