Skip to content

Commit 29309cc

Browse files
committed
Merge pull request #88 from dbarfield/jsonpNullLists
[BUG] Handle nulls in JSON arrays
2 parents 2d26443 + 50cae61 commit 29309cc

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)