diff --git a/src/zutil/ClassUtil.java b/src/zutil/ClassUtil.java index a7de0cc..0ad2cd8 100644 --- a/src/zutil/ClassUtil.java +++ b/src/zutil/ClassUtil.java @@ -24,6 +24,10 @@ package zutil; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; import java.util.HashSet; /** @@ -77,4 +81,17 @@ public class ClassUtil { public static boolean isPrimitive(Class type){ return primitives.contains( type ); } + + + public static Class[] getGenericClasses(Field field){ + Class[] classArray = new Class[0]; + Type genericFieldType = field.getGenericType(); + + if(genericFieldType instanceof ParameterizedType){ + ParameterizedType aType = (ParameterizedType) genericFieldType; + Type[] fieldArgTypes = aType.getActualTypeArguments(); + classArray = Arrays.copyOf(fieldArgTypes, fieldArgTypes.length, Class[].class); + } + return classArray; + } } diff --git a/src/zutil/parser/DataNode.java b/src/zutil/parser/DataNode.java index b6c9838..34a8bd8 100644 --- a/src/zutil/parser/DataNode.java +++ b/src/zutil/parser/DataNode.java @@ -34,7 +34,7 @@ import java.util.*; */ public class DataNode implements Iterable{ public enum DataType{ - Map, List, String, Number, Boolean, Null + Map, List, String, Number, Boolean } private Map map = null; private List list = null; @@ -249,9 +249,6 @@ public class DataNode implements Iterable{ public boolean isValue(){ return type != DataType.Map && type != DataType.List; } - public boolean isNull(){ - return type == DataType.Null; - } /** * @return the type of the node */ diff --git a/src/zutil/parser/json/JSONObjectInputStream.java b/src/zutil/parser/json/JSONObjectInputStream.java index 9f03a11..40a0817 100644 --- a/src/zutil/parser/json/JSONObjectInputStream.java +++ b/src/zutil/parser/json/JSONObjectInputStream.java @@ -25,6 +25,7 @@ package zutil.parser.json; import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import zutil.ClassUtil; import zutil.log.LogUtil; import zutil.parser.Base64Decoder; import zutil.parser.DataNode; @@ -121,6 +122,47 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C } + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected Object readType(Class type, Class[] genType, String key, DataNode json) throws IllegalAccessException, ClassNotFoundException, InstantiationException, UnsupportedDataTypeException, NoSuchFieldException { + // Field type is a primitive? + if(type.isPrimitive() || String.class.isAssignableFrom(type)){ + return readPrimitive(type, json); + } + else if(type.isArray()){ + if(type.getComponentType() == Byte.class) + return Base64Decoder.decodeToByte(json.getString()); + else{ + Object array = Array.newInstance(type.getComponentType(), json.size()); + for(int i=0; i it=json.keyIterator(); it.hasNext();){ + String subKey = it.next(); + map.put( + subKey, + readType(genType[1], null, subKey, json.get(subKey))); + } + return map; + } + // Field is a new Object + else{ + return readObject(type, key, json); + } + } + + protected Object readObject(Class type, String key, DataNode json) throws IllegalAccessException, InstantiationException, ClassNotFoundException, IllegalArgumentException, UnsupportedDataTypeException, NoSuchFieldException { // Only parse if json is a map if(!json.isMap()) @@ -158,8 +200,9 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C json.get(field.getName()) != null){ // Parse field field.setAccessible(true); - field.set(obj, readField( + field.set(obj, readType( field.getType(), + ClassUtil.getGenericClasses(field), field.getName(), json.get(field.getName()))); } @@ -170,46 +213,6 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C return obj; } - @SuppressWarnings({ "rawtypes", "unchecked" }) - protected Object readField(Class type, String key, DataNode json) throws IllegalAccessException, ClassNotFoundException, InstantiationException, UnsupportedDataTypeException, NoSuchFieldException { - // Field type is a primitive? - if(type.isPrimitive() || String.class.isAssignableFrom(type)){ - return readPrimitive(type, json); - } - else if(type.isArray()){ - if(type.getComponentType() == Byte.class) - return Base64Decoder.decodeToByte(json.getString()); - else{ - Object array = Array.newInstance(type.getComponentType(), json.size()); - for(int i=0; i it=json.keyIterator(); it.hasNext();){ - String subKey = it.next(); - map.put( - subKey, - readObject(null, subKey, json.get(subKey))); - } - return map; - } - // Field is a new Object - else{ - return readObject(type, key, json); - } - } - protected static Object readPrimitive(Class type, DataNode json){ if (type == int.class || diff --git a/src/zutil/parser/json/JSONObjectOutputStream.java b/src/zutil/parser/json/JSONObjectOutputStream.java index 8238b0d..dba4469 100644 --- a/src/zutil/parser/json/JSONObjectOutputStream.java +++ b/src/zutil/parser/json/JSONObjectOutputStream.java @@ -123,10 +123,22 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput root.set(field.getName(), arrayNode); } else if(List.class.isAssignableFrom(field.getType())){ - // TODO Add List Support + DataNode listNode = new DataNode(DataNode.DataType.List); + List list = (List)fieldObj; + for(Object item : list){ + listNode.add(getDataNode(item)); + } + root.set(field.getName(), listNode); } else if(Map.class.isAssignableFrom(field.getType())){ - // TODO Add Map Support + DataNode mapNode = new DataNode(DataNode.DataType.Map); + Map map = (Map)fieldObj; + for(Object key : map.keySet()){ + mapNode.set( + getDataNode(key).getString(), + getDataNode(map.get(key))); + } + root.set(field.getName(), mapNode); } else{ root.set(field.getName(), getDataNode(fieldObj)); diff --git a/src/zutil/parser/json/JSONParser.java b/src/zutil/parser/json/JSONParser.java index 6a536a6..fded672 100644 --- a/src/zutil/parser/json/JSONParser.java +++ b/src/zutil/parser/json/JSONParser.java @@ -140,7 +140,7 @@ public class JSONParser extends Parser { // Check what type of type the data is String data = tmp.toString().trim(); if( NULL_PATTERN.matcher(data).matches() ) - root = new DataNode(DataType.Null); + root = null; else if( BOOLEAN_PATTERN.matcher(data).matches() ) root = new DataNode(DataType.Boolean); else if( NUMBER_PATTERN.matcher(data).matches() ) @@ -149,7 +149,8 @@ public class JSONParser extends Parser { root = new DataNode(DataType.String); data = unEscapeString(data); } - root.set(data); + if(root != null) + root.set(data); break; } diff --git a/test/zutil/test/JSONSerializerTest.java b/test/zutil/test/JSONSerializerTest.java index f1813bc..34bbb79 100644 --- a/test/zutil/test/JSONSerializerTest.java +++ b/test/zutil/test/JSONSerializerTest.java @@ -32,7 +32,9 @@ import zutil.parser.json.JSONObjectOutputStream; import java.io.IOException; import java.io.Serializable; import java.io.StringReader; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import static org.junit.Assert.assertEquals; @@ -124,6 +126,33 @@ public class JSONSerializerTest{ TestClass.assertEquals(sourceObj, targetObj); } + @Test + public void testSerializerWithMapField() throws InterruptedException, IOException, ClassNotFoundException{ + TestClassMap sourceObj = new TestClassMap().init(); + + String data = writeObjectToJson(sourceObj, false); + data = data.replace("\"", "'"); + assertEquals( + "{'map': {'key2': 'value2', 'key1': 'value1'}}", + data); + + TestClassMap targetObj = sendReceiveObject(sourceObj); + TestClassMap.assertEquals(sourceObj, targetObj); + } + + @Test + public void testSerializerWithListField() throws InterruptedException, IOException, ClassNotFoundException{ + TestClassList sourceObj = new TestClassList().init(); + + String data = writeObjectToJson(sourceObj, false); + data = data.replace("\"", "'"); + assertEquals( + "{'list': ['value1', 'value2']}", + data); + + TestClassList targetObj = sendReceiveObject(sourceObj); + TestClassList.assertEquals(sourceObj, targetObj); + } /******************* Utility Functions ************************************/ @@ -155,7 +184,7 @@ public class JSONSerializerTest{ out.close(); String data = bout.toString(); - //System.out.println(data); + System.out.println(data); return data; } @@ -244,4 +273,34 @@ public class JSONSerializerTest{ Arrays.equals(this.array ,((TestClassStringArray)obj).array); } } + + public static class TestClassMap{ + private HashMap map; + + public TestClassMap init(){ + map = new HashMap<>(); + map.put("key1", "value1"); + map.put("key2", "value2"); + return this; + } + + public static void assertEquals(TestClassMap obj1, TestClassMap obj2){ + Assert.assertEquals(obj1.map, obj2.map); + } + } + + public static class TestClassList{ + private ArrayList list; + + public TestClassList init(){ + list = new ArrayList<>(); + list.add("value1"); + list.add("value2"); + return this; + } + + public static void assertEquals(TestClassList obj1, TestClassList obj2){ + Assert.assertEquals(obj1.list, obj2.list); + } + } }