Skip to content

Commit 4c6598e

Browse files
committed
fix: explicitly serialize null values found in dynamic properties
1 parent 4eb615c commit 4c6598e

File tree

2 files changed

+40
-14
lines changed

2 files changed

+40
-14
lines changed

src/main/java/com/ibm/cloud/sdk/core/util/DynamicModelTypeAdapterFactory.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* (C) Copyright IBM Corp. 2019.
2+
* (C) Copyright IBM Corp. 2019, 2020.
33
* Copyright (C) 2011 Google Inc.
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
@@ -67,6 +67,9 @@
6767
*
6868
* <p>This class includes code that was adapted from the internal <code>ReflectiveTypeAdapterFactory</code> and
6969
* <code>MapTypeAdapterFactory</code> classes from Gson.
70+
*
71+
* <p>This class will explicitly serialize null values found within dynamic (additional) properties, regardless of
72+
* the global "serialize nulls" setting in Gson.
7073
*/
7174
public class DynamicModelTypeAdapterFactory implements TypeAdapterFactory {
7275
private static final Logger LOGGER = Logger.getLogger(DynamicModelTypeAdapterFactory.class.getName());
@@ -331,9 +334,17 @@ public void write(JsonWriter out, T value) throws IOException {
331334
}
332335

333336
// Next, serialize each of the map entries.
334-
for (String key : ((DynamicModel<?>) value).getPropertyNames()) {
335-
out.name(String.valueOf(key));
336-
mapValueTypeAdapter.write(out, ((DynamicModel<?>) value).get(key));
337+
// When serializing the map entries (i.e. additional/dynamic properties) we want
338+
// to explicitly serialize null values regardless of the global Gson "serialize nulls" setting.
339+
boolean serializeNulls = out.getSerializeNulls();
340+
out.setSerializeNulls(true);
341+
try {
342+
for (String key : ((DynamicModel<?>) value).getPropertyNames()) {
343+
out.name(String.valueOf(key));
344+
mapValueTypeAdapter.write(out, ((DynamicModel<?>) value).get(key));
345+
}
346+
} finally {
347+
out.setSerializeNulls(serializeNulls);
337348
}
338349
} catch (IllegalAccessException e) {
339350
throw new AssertionError(e);

src/test/java/com/ibm/cloud/sdk/core/test/model/DynamicModelSerializationTest.java

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* (C) Copyright IBM Corp. 2015, 2019.
2+
* (C) Copyright IBM Corp. 2015, 2020.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
55
* the License. You may obtain a copy of the License at
@@ -54,15 +54,18 @@ private <T> T deserialize(String json, Class<T> clazz) {
5454
return GsonSingleton.getGson().fromJson(json, clazz);
5555
}
5656

57-
private <T> void testSerDeser(DynamicModel<?> model, Class<T> clazz) {
58-
String jsonString = serialize(model);
57+
private void display(String msg) {
5958
if (displayOutput) {
60-
System.out.println("serialized " + model.getClass().getSimpleName() + ": " + jsonString);
59+
System.out.println(msg);
6160
}
61+
}
62+
63+
private <T> void testSerDeser(DynamicModel<?> model, Class<T> clazz) {
64+
String jsonString = serialize(model);
65+
66+
display("serialized " + model.getClass().getSimpleName() + ": " + jsonString);
6267
T newModel = deserialize(jsonString, clazz);
63-
if (displayOutput) {
64-
System.out.println("de-serialized " + model.getClass().getSimpleName() + ": " + newModel.toString());
65-
}
68+
display("de-serialized " + model.getClass().getSimpleName() + ": " + newModel.toString());
6669
assertEquals(newModel, model);
6770
}
6871

@@ -203,19 +206,31 @@ public void testNoCtor() {
203206
public void testNullValues() {
204207
ModelAPFoo model = createModelAPFoo();
205208
model.setProp1(null);
206-
// model.put("basketball", "foo");
207209
testSerDeser(model, ModelAPFoo.class);
208210
}
209211

212+
@Test
213+
public void testAddlPropsNull() {
214+
ModelAPString model = createModelAPString();
215+
model.put("basketball", null);
216+
217+
String json = serialize(model);
218+
display("Serialized: " + json);
219+
assertTrue(json.contains("\"basketball\": null"));
220+
221+
ModelAPString newModel = deserialize(json, ModelAPString.class);
222+
assertEquals(newModel, model);
223+
}
224+
210225
@Test(expectedExceptions = {JsonSyntaxException.class})
211226
public void testBadDeser() {
212227

213228
// Obtain the json string and then render it incorrect to trigger a deserialization error.
214229
ModelAPFoo model = createModelAPFoo();
215230
String goodJson = serialize(model);
216-
if (displayOutput) System.out.println("Serialized ModelAPFoo: " + goodJson);
231+
display("Serialized ModelAPFoo: " + goodJson);
217232
String badJson = goodJson.replaceAll("foo", "FOO").replaceAll("prop2", "var2");
218-
if (displayOutput) System.out.println("Incorrect JSON: " + badJson);
233+
display("Incorrect JSON: " + badJson);
219234

220235
// We just need to try to deserialize the bad JSON string to trigger the exception.
221236
deserialize(badJson, ModelAPFoo.class);

0 commit comments

Comments
 (0)