Skip to content

Commit 50cae61

Browse files
committed
[BUG] Handle nulls in JSON arrays
This fix handles the bug where a null is in a JSON array that is being deserialized. It also tidies up the code within the DataModelSerializer.processJsonArray to remove some of the nesting. Tests are added for deserializing of nulls and nulls in lists as well as clarifying the support for JSON true/false.
1 parent 2d26443 commit 50cae61

2 files changed

Lines changed: 179 additions & 35 deletions

File tree

client-lib-tests/src/test/java/com/ibm/ws/repository/transport/client/test/DataModelSerializerTest.java

Lines changed: 139 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
import java.io.ByteArrayInputStream;
2626
import java.io.IOException;
2727
import java.io.StringReader;
28+
import java.util.ArrayList;
2829
import java.util.Arrays;
2930
import java.util.Collection;
31+
import java.util.List;
3032
import java.util.Locale;
3133

3234
import javax.json.Json;
@@ -353,25 +355,151 @@ public void testDeserializeOkWithUnwantedFieldPresent() throws IOException, BadV
353355
}
354356
}
355357

356-
public static class NullFieldTest {
357-
String nullField;
358+
public static class DeserialisationHelperClass {
359+
int intField;
360+
String stringField;
361+
boolean booleanField;
362+
List<String> stringList;
363+
List<Boolean> booleanList;
364+
List<Integer> integerList;
358365

359-
public String getNullField() {
360-
return nullField;
366+
public int getIntField() {
367+
return intField;
361368
}
362369

363-
public void setNullField(String nullField) {
364-
this.nullField = nullField;
370+
public void setIntField(int intField) {
371+
this.intField = intField;
372+
}
373+
374+
public List<Integer> getIntegerList() {
375+
return integerList;
376+
}
377+
378+
public void setIntegerList(List<Integer> integerList) {
379+
this.integerList = integerList;
380+
}
381+
382+
public List<Boolean> getBooleanList() {
383+
return booleanList;
384+
}
385+
386+
public void setBooleanList(List<Boolean> booleanList) {
387+
this.booleanList = booleanList;
388+
}
389+
390+
public String getStringField() {
391+
return stringField;
392+
}
393+
394+
public void setStringField(String stringField) {
395+
this.stringField = stringField;
396+
}
397+
398+
public boolean getBooleanField() {
399+
return booleanField;
400+
}
401+
402+
public void setBooleanField(boolean booleanField) {
403+
this.booleanField = booleanField;
404+
}
405+
406+
public List<String> getStringList() {
407+
return stringList;
408+
}
409+
410+
public void setStringList(List<String> stringList) {
411+
this.stringList = stringList;
365412
}
366413
}
367414

368415
@Test
369-
public void testDeserializeNull() throws Exception {
416+
public void deserializePrimitives() throws Exception {
417+
418+
DeserialisationHelperClass dhc = DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"intField\": 25 }".getBytes()),
419+
DeserialisationHelperClass.class);
420+
assertEquals("dummy text", 25, dhc.getIntField());
421+
}
370422

371-
NullFieldTest nft = DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"nullField\": null }".getBytes()),
372-
NullFieldTest.class);
373-
assertEquals("Null field was not set correctly during null test,",
374-
null, nft.getNullField());
423+
@Test
424+
public void testDeserializeIntegers() throws Exception {
425+
426+
// deserialize a single integer
427+
try {
428+
DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"integerField\": 99 }".getBytes()),
429+
DeserialisationHelperClass.class);
430+
fail("deserializing an Integer should have thrown an exception as we don't support them");
431+
} catch (IllegalArgumentException iae) {
432+
if (!iae.getMessage().contains("Data Model Error: unable to invoke setter for data model element")) {
433+
fail("unrecognised exception text");
434+
}
435+
// expected
436+
}
437+
438+
// deserialize a list of Integers
439+
try {
440+
DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"integerList\": [1,2,3] }".getBytes()),
441+
DeserialisationHelperClass.class);
442+
fail("deserializing an integer in a list should have thrown an exception");
443+
} catch (IllegalStateException ise) {
444+
assertEquals("Unexpected exception tests message,", DataModelSerializer.DATA_MODEL_ERROR_NUMBER, ise.getMessage());
445+
}
446+
447+
}
448+
449+
@Test
450+
public void testDeserializeNulls() throws Exception {
451+
452+
// simple string list sanity check
453+
DeserialisationHelperClass dhc = DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"stringList\": [\"somestring1\", \"somestring2\"] }".getBytes()),
454+
DeserialisationHelperClass.class);
455+
List<String> list = new ArrayList<String>();
456+
list.add("somestring1");
457+
list.add("somestring2");
458+
assertEquals("List was not set correctly from JSON list,", list, dhc.getStringList());
459+
460+
// test a field set to null
461+
dhc = DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"stringField\": null }".getBytes()),
462+
DeserialisationHelperClass.class);
463+
assertEquals("Field was not correctly set to null from a null JSON field,", null, dhc.getStringField());
464+
465+
// test a list set to null
466+
dhc = DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"stringList\": null }".getBytes()),
467+
DeserialisationHelperClass.class);
468+
assertEquals("List was not correctly set to null from a null JSON list,", null, dhc.getStringList());
469+
470+
// list containing nulls
471+
dhc = DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"stringList\": [ null, null ] }".getBytes()),
472+
DeserialisationHelperClass.class);
473+
list = new ArrayList<String>();
474+
list.add(null);
475+
list.add(null);
476+
assertEquals("List containing nulls was not correctly set from JSON string,", list, dhc.getStringList());
477+
}
478+
479+
@Test
480+
public void testDeserializeBooleans() throws Exception {
481+
482+
// add false to a boolean field
483+
DeserialisationHelperClass dhc = DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"booleanField\": false }".getBytes()),
484+
DeserialisationHelperClass.class);
485+
assertEquals("Boolean field not set correctly test,",
486+
false, dhc.getBooleanField());
487+
488+
// We don't support list containing JSON true / false
489+
try {
490+
dhc = DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"booleanList\": [ true ] }".getBytes()),
491+
DeserialisationHelperClass.class);
492+
fail("Deserializing a List containing true should have thrown an IllegalStateException");
493+
} catch (IllegalStateException ise) {
494+
assertEquals("Incorrect IllegalStateException caught,", DataModelSerializer.DATA_MODEL_ERROR_ARRAY, ise.getMessage());
495+
}
496+
try {
497+
dhc = DataModelSerializer.deserializeObject(new ByteArrayInputStream("{ \"booleanList\": [ false ] }".getBytes()),
498+
DeserialisationHelperClass.class);
499+
fail("Deserializing a List containing false should have thrown an IllegalStateException");
500+
} catch (IllegalStateException ise) {
501+
assertEquals("Incorrect IllegalStateException caught,", DataModelSerializer.DATA_MODEL_ERROR_ARRAY, ise.getMessage());
502+
}
375503
}
376504

377505
public static class LocaleTest {
@@ -440,9 +568,7 @@ public void testJSONIgnoreGetter() throws Exception {
440568
String data = DataModelSerializer.serializeAsString(ignore);
441569
assertTrue("Getter wasn't invoked when it should have been, so the data should contain wibble fish monkey",
442570
data.contains(testString));
443-
System.out.println("Data is " + data);
444571
JSONIgnoreSetterTest gotBack = DataModelSerializer.deserializeObject(new ByteArrayInputStream(data.getBytes()), JSONIgnoreSetterTest.class);
445-
System.out.println("Read back " + gotBack.getTestData());
446572
assertNull("Setter was invoked when it should not have been, so the data should be null", gotBack.getTestData());
447573
}
448574

client-lib/src/main/java/com/ibm/ws/repository/transport/client/DataModelSerializer.java

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ public class DataModelSerializer {
7676
private static final String DATE_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'";
7777
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
7878

79+
// Make processJsonArray Exception messages visible for test use
80+
public final static String DATA_MODEL_ERROR_ARRAY = "Data Model Error: Simple parser does not understand nested arrays, or true/false in an array.";
81+
public final static String DATA_MODEL_ERROR_NUMBER = "Data Model Error: Simple parser does not understand numbers in arrays.";
82+
7983
public static volatile boolean IGNORE_UNKNOWN_FIELDS = true;
8084

8185
public static enum Verification {
@@ -679,7 +683,9 @@ private static <T> T processJsonObjectBackIntoDataModelInstance(JsonObject json,
679683
} else if (fieldType.cls.equals(String.class)) {
680684
invokeSetter(fieldType.m, targetObject, valueString);
681685
} else {
682-
invokeSetter(fieldType.m, targetObject, value);
686+
throw new IllegalArgumentException("Data Model Error: unable to invoke setter for data model element "
687+
+ fieldType.m.getName() + " on "
688+
+ targetObject.getClass().getName());
683689
}
684690
}
685691
}
@@ -699,27 +705,39 @@ private static <T> T processJsonObjectBackIntoDataModelInstance(JsonObject json,
699705
* @throws IOException
700706
*/
701707
private static <T> void processJsonArray(JsonArray jsonArray, List<T> list, Class<? extends T> listClass, Verification verify, ListVersionHandling versionHandler) throws IOException, BadVersionException {
702-
for (Object o : jsonArray) {
703-
if (o instanceof JsonArray) {
704-
//array had another array as an element.
705-
throw new IllegalStateException("Data Model Error: Simple parser does not understand nested arrays");
706-
} else if (o instanceof JsonObject) {
707-
//array had a complex object as an element.
708-
try {
709-
T newArrayElement = processJsonObjectBackIntoDataModelInstance((JsonObject) o, listClass, verify);
710-
list.add(newArrayElement);
711-
} catch (BadVersionException e) {
712-
// versionHandler tells us what to do...
713-
if (!ListVersionHandling.IGNORE_ELEMENT.equals(versionHandler)) {
714-
// Default and THROW_EXCEPTION mean we should rethrow this
715-
throw e;
716-
} // Else ignore this
717-
}
718-
} else if (o instanceof JsonString) {
719-
list.add(listClass.cast(((JsonString) o).getString()));
720-
} else {
721-
//array had a primitive as an element..
722-
list.add(listClass.cast(o));
708+
709+
for (JsonValue value : jsonArray) {
710+
switch (value.getValueType()) {
711+
case TRUE:
712+
case FALSE:
713+
case ARRAY:
714+
// array had another array as an element or a JSON true/false.
715+
// message reads "Data Model Error: Simple parser does not understand nested arrays, or true/false in an array."
716+
throw new IllegalStateException(DATA_MODEL_ERROR_ARRAY);
717+
case OBJECT:
718+
//array had a complex object as an element.
719+
try {
720+
T newArrayElement = processJsonObjectBackIntoDataModelInstance((JsonObject) value, listClass, verify);
721+
list.add(newArrayElement);
722+
} catch (BadVersionException e) {
723+
// versionHandler tells us what to do...
724+
if (!ListVersionHandling.IGNORE_ELEMENT.equals(versionHandler)) {
725+
// Default and THROW_EXCEPTION mean we should rethrow this
726+
throw e;
727+
} // Else ignore this
728+
}
729+
break;
730+
case STRING:
731+
list.add(listClass.cast(((JsonString) value).getString()));
732+
break;
733+
case NULL:
734+
list.add(null);
735+
break;
736+
case NUMBER:
737+
// message reads "Data Model Error: Simple parser does not understand numbers in arrays.";
738+
throw new IllegalStateException(DATA_MODEL_ERROR_NUMBER);
739+
default:
740+
throw new IllegalStateException("Data Model Error: Unknown Json value type returned: " + value.getValueType().toString());
723741
}
724742
}
725743
}

0 commit comments

Comments
 (0)