Skip to content

Commit acac9a7

Browse files
committed
For forward compatibility and prevent deserialization exceptions, allow unknown enums to be deserialized to null
1 parent 702c683 commit acac9a7

File tree

3 files changed

+112
-1
lines changed

3 files changed

+112
-1
lines changed

src/main/java/com/taboola/rest/api/internal/config/SerializationConfig.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public class SerializationConfig {
88
private boolean shouldIgnoreAnySetterAnnotation;
99
private boolean shouldDisableReadUnknownEnumValuesAsDefaultValue;
1010
private boolean shouldUseSnakeCase;
11+
private boolean shouldAllowNullAsDefaultValueForReadUnknownEnums;
1112

1213
public SerializationConfig() {
1314
mixins = new HashMap<>();
@@ -36,6 +37,11 @@ public SerializationConfig setShouldUseSnakeCase() {
3637
return this;
3738
}
3839

40+
public SerializationConfig setShouldAllowNullAsDefaultValueForReadUnknownEnums() {
41+
this.shouldAllowNullAsDefaultValueForReadUnknownEnums = true;
42+
return this;
43+
}
44+
3945
public Map<Class<?>, Class<?>> getMixins() {
4046
return mixins;
4147
}
@@ -50,12 +56,19 @@ public boolean shouldUseSnakeCase() {
5056
return shouldUseSnakeCase;
5157
}
5258

59+
public boolean shouldAllowNullAsDefaultValueForReadUnknownEnums() {
60+
return shouldAllowNullAsDefaultValueForReadUnknownEnums;
61+
}
62+
63+
5364
@Override
5465
public String toString() {
5566
return "SerializationConfig{" +
5667
"mixins=" + mixins +
5768
", shouldIgnoreAnySetterAnnotation=" + shouldIgnoreAnySetterAnnotation +
69+
", shouldDisableReadUnknownEnumValuesAsDefaultValue=" + shouldDisableReadUnknownEnumValuesAsDefaultValue +
5870
", shouldUseSnakeCase=" + shouldUseSnakeCase +
71+
", shouldAllowNullAsDefaultValueForReadUnknownEnums=" + shouldAllowNullAsDefaultValueForReadUnknownEnums +
5972
'}';
6073
}
6174
}

src/main/java/com/taboola/rest/api/internal/serialization/SerializationMapperCreator.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ public static ObjectMapper createObjectMapper(SerializationConfig serializationC
2020
if (!serializationConfig.shouldDisableReadUnknownEnumValuesAsDefaultValue()) {
2121
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);
2222
}
23+
if (serializationConfig.shouldAllowNullAsDefaultValueForReadUnknownEnums()) {
24+
// NOTE: default enum value takes precedence over null
25+
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
26+
}
2327

2428
serializationConfig.getMixins().forEach(objectMapper::addMixIn);
2529

src/test/java/com/taboola/rest/api/internal/serialization/SerializationMapperCreatorTest.java

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,36 @@ public void createObjectMapper_defaultSerializationConfigAndApiObjectHasInvalidE
9090
Assert.assertEquals("Unknown enum is parsed incorrectly", SampleEnum.UNKNOWN, sampleApi.letter);
9191
}
9292

93+
@Test
94+
public void createObjectMapper_serializationConfigAndAllowNullAsDefaultValueForReadUnknownEnumsAndApiObjectHasInvalidEnum_defaultValueUsedOnSerialization() throws IOException {
95+
SerializationConfig serializationConfig = new SerializationConfig().setShouldAllowNullAsDefaultValueForReadUnknownEnums();
96+
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
97+
98+
SampleApi sampleApi = objectMapper.readValue("{ \"letter\": \"D\" }", SampleApi.class);
99+
100+
Assert.assertEquals("Unknown enum is parsed incorrectly. Default takes precedence before null", SampleEnum.UNKNOWN, sampleApi.letter);
101+
}
102+
93103
@Test
94104
public void createObjectMapper_defaultSerializationConfigAndApiObjectHasEmptyEnumValue_defaultValueUsedOnSerialization() throws IOException {
95105
SerializationConfig serializationConfig = new SerializationConfig();
96106
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
97107

98-
SampleApi sampleApi = objectMapper.readValue("{ \"letter\": \"D\" }", SampleApi.class);
108+
SampleApi sampleApi = objectMapper.readValue("{ \"letter\": \"\" }", SampleApi.class);
99109

100110
Assert.assertEquals("Unknown enum is parsed incorrectly", SampleEnum.UNKNOWN, sampleApi.letter);
101111
}
102112

113+
@Test
114+
public void createObjectMapper_defaultSerializationConfigAndApiObjectHasNullEnumValue_nullOnSerialization() throws IOException {
115+
SerializationConfig serializationConfig = new SerializationConfig();
116+
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
117+
118+
SampleApi sampleApi = objectMapper.readValue("{ \"letter\": null }", SampleApi.class);
119+
120+
Assert.assertNull("Unknown enum is parsed incorrectly", sampleApi.letter);
121+
}
122+
103123
@Test
104124
public void createObjectMapper_serializationConfigWithReadUnknownEnumsDisabledAndApiObjectHasValidEnum_enumParsedCorrectly() throws IOException {
105125
SerializationConfig serializationConfig = new SerializationConfig().setShouldDisableReadUnknownEnumValuesAsDefaultValue();
@@ -118,6 +138,16 @@ public void createObjectMapper_serializationConfigWithReadUnknownEnumsDisabledAn
118138
objectMapper.readValue("{ \"letter\": \"D\" }", SampleApi.class);
119139
}
120140

141+
@Test
142+
public void createObjectMapper_serializationConfigWithReadUnknownEnumsDisabledAndAllowNullAsDefaultValueForReadUnknownEnumsAndApiObjectHasInvalidEnum_nullOnSerialization() throws IOException {
143+
SerializationConfig serializationConfig = new SerializationConfig().setShouldDisableReadUnknownEnumValuesAsDefaultValue().setShouldAllowNullAsDefaultValueForReadUnknownEnums();
144+
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
145+
146+
SampleApi sampleApi = objectMapper.readValue("{ \"letter\": \"D\" }", SampleApi.class);
147+
148+
Assert.assertNull("Unknown enum is parsed incorrectly", sampleApi.letter);
149+
}
150+
121151
@Test(expected = InvalidFormatException.class)
122152
public void createObjectMapper_serializationConfigWithReadUnknownEnumsDisabledAndApiObjectHasEmptyEnumValue_InvalidFormatExceptionIsThrown() throws IOException {
123153
SerializationConfig serializationConfig = new SerializationConfig().setShouldDisableReadUnknownEnumValuesAsDefaultValue();
@@ -126,9 +156,70 @@ public void createObjectMapper_serializationConfigWithReadUnknownEnumsDisabledAn
126156
objectMapper.readValue("{ \"letter\": \"\" }", SampleApi.class);
127157
}
128158

159+
@Test
160+
public void createObjectMapper_serializationConfigWithReadUnknownEnumsDisabledAndAllowNullAsDefaultValueForReadUnknownEnumsAndApiObjectHasEmptyEnumValue_nullOnSerialization() throws IOException {
161+
SerializationConfig serializationConfig = new SerializationConfig().setShouldDisableReadUnknownEnumValuesAsDefaultValue().setShouldAllowNullAsDefaultValueForReadUnknownEnums();
162+
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
163+
164+
SampleApi sampleApi = objectMapper.readValue("{ \"letter\": \"\" }", SampleApi.class);
165+
166+
Assert.assertNull("Unknown enum is parsed incorrectly", sampleApi.letter);
167+
}
168+
169+
@Test
170+
public void createObjectMapper_defaultSerializationConfigAndNoDefaultEnumIsValid_enumParsedCorrectly() throws IOException {
171+
SerializationConfig serializationConfig = new SerializationConfig();
172+
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
173+
174+
SampleApi sampleApi = objectMapper.readValue("{ \"letterNoDefault\": \"BB\" }", SampleApi.class);
175+
176+
Assert.assertEquals("shouldAllowNullAsDefaultValueForReadUnknownEnums should not affect deserializing valid enums",
177+
SampleEnumNoDefault.BB, sampleApi.letterNoDefault);
178+
}
179+
180+
@Test
181+
public void createObjectMapper_defaultSerializationConfigAndNoDefaultEnumIsNull_enumIsNull() throws IOException {
182+
SerializationConfig serializationConfig = new SerializationConfig();
183+
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
184+
185+
SampleApi sampleApi = objectMapper.readValue("{ \"letterNoDefault\": null }", SampleApi.class);
186+
187+
Assert.assertNull("shouldAllowNullAsDefaultValueForReadUnknownEnums should not affect deserializing null enums", sampleApi.letterNoDefault);
188+
}
189+
190+
@Test(expected = InvalidFormatException.class)
191+
public void createObjectMapper_defaultSerializationConfigAndNoDefaultEnumIsEmpty_InvalidFormatExceptionIsThrown() throws IOException {
192+
SerializationConfig serializationConfig = new SerializationConfig();
193+
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
194+
195+
objectMapper.readValue("{ \"letterNoDefault\": \"\" }", SampleApi.class);
196+
}
197+
198+
@Test(expected = InvalidFormatException.class)
199+
public void createObjectMapper_defaultSerializationConfigAndNoDefaultEnumIsInvalid_InvalidFormatExceptionIsThrown() throws IOException {
200+
SerializationConfig serializationConfig = new SerializationConfig();
201+
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
202+
203+
objectMapper.readValue("{ \"letterNoDefault\": \"BBB\" }", SampleApi.class);
204+
}
205+
206+
@Test
207+
public void createObjectMapper_serializationConfigAndAllowNullAsDefaultValueForReadUnknownEnumsAndNoDefaultEnumIsInvalid_enumIsNull() throws IOException {
208+
SerializationConfig serializationConfig = new SerializationConfig().setShouldAllowNullAsDefaultValueForReadUnknownEnums();
209+
ObjectMapper objectMapper = SerializationMapperCreator.createObjectMapper(serializationConfig);
210+
211+
SampleApi sampleApi = objectMapper.readValue("{ \"letterNoDefault\": \"BBB\" }", SampleApi.class);
212+
213+
Assert.assertNull("Even though enum has no set default value and deserialization not configured to use default value, null should be default",
214+
sampleApi.letterNoDefault);
215+
}
216+
129217
private enum SampleEnum {
130218
A, B, @JsonEnumDefaultValue UNKNOWN
131219
}
220+
private enum SampleEnumNoDefault {
221+
AA, BB
222+
}
132223

133224
private static class SampleApi {
134225
@JsonProperty("id")
@@ -137,6 +228,9 @@ private static class SampleApi {
137228
@JsonProperty("letter")
138229
SampleEnum letter;
139230

231+
@JsonProperty("letterNoDefault")
232+
SampleEnumNoDefault letterNoDefault;
233+
140234
@JsonProperty("name")
141235
String name;
142236

0 commit comments

Comments
 (0)