From 67483895c33869f99e595f603e00e99029ebb650 Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Mon, 12 Jul 2021 22:39:36 +0200 Subject: [PATCH] Added support for Enum and HasSets to JSON serializer --- src/zutil/db/bean/DBBeanSQLResultHandler.java | 2 +- .../parser/json/JSONObjectInputStream.java | 57 +++++++++--------- .../parser/json/JSONObjectOutputStream.java | 13 +++-- .../zutil/parser/json/JSONSerializerTest.java | 58 +++++++++++-------- 4 files changed, 71 insertions(+), 59 deletions(-) diff --git a/src/zutil/db/bean/DBBeanSQLResultHandler.java b/src/zutil/db/bean/DBBeanSQLResultHandler.java index fce89d2..aa4b2e9 100755 --- a/src/zutil/db/bean/DBBeanSQLResultHandler.java +++ b/src/zutil/db/bean/DBBeanSQLResultHandler.java @@ -149,7 +149,7 @@ public class DBBeanSQLResultHandler implements SQLResultHandler { if (obj == null) { // Cache miss create a new bean logger.fine("Creating new Bean(" + beanClass.getName() + ") with id: " + id); - obj = beanClass.newInstance(); + obj = beanClass.getDeclaredConstructor().newInstance(); obj.setId(id); updateBean(result, obj); } else if (DBBeanCache.isOutDated(obj)) { diff --git a/src/zutil/parser/json/JSONObjectInputStream.java b/src/zutil/parser/json/JSONObjectInputStream.java index 9125ca6..3f40460 100755 --- a/src/zutil/parser/json/JSONObjectInputStream.java +++ b/src/zutil/parser/json/JSONObjectInputStream.java @@ -32,11 +32,9 @@ import zutil.parser.DataNode; import java.io.*; import java.lang.reflect.Array; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -47,20 +45,15 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C protected static final String MD_CLASS = "@class"; private JSONParser parser; - private HashMap registeredClasses; - private HashMap objectCache; + private HashMap registeredClasses = new HashMap<>(); + private HashMap objectCache = new HashMap<>(); - private JSONObjectInputStream() { - this.registeredClasses = new HashMap<>(); - this.objectCache = new HashMap<>(); - } + private JSONObjectInputStream() {} public JSONObjectInputStream(Reader in) { - this(); this.parser = new JSONParser(in); } public JSONObjectInputStream(InputStream in) { - this(); this.parser = new JSONParser(in); } @@ -110,7 +103,7 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C try { DataNode root = parser.read(); if (root != null) { - return (T)readObject(c, null, root); + return (T) readObject(c, null, root); } } catch (Exception e) { logger.log(Level.WARNING, null, e); @@ -122,13 +115,19 @@ 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, NoSuchFieldException { + protected Object readType(Class type, Class[] genericType, String key, DataNode json) throws IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException { + // TODO: Don't replace the existing object if it exists + if (json == null || type == null) return null; // Field type is a primitive? - if (type.isPrimitive() || String.class.isAssignableFrom(type)) { + if (ClassUtil.isPrimitive(type) || + ClassUtil.isWrapper(type)) { return readPrimitive(type, json); } + else if (type.isEnum()) { + return Enum.valueOf((Class)type, (String) readPrimitive(String.class, json)); + } else if (type.isArray()) { if (type.getComponentType() == byte.class) return Base64Decoder.decodeToByte(json.getString()); @@ -140,25 +139,27 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C return array; } } - else if (List.class.isAssignableFrom(type)) { - if (genType == null || genType.length < 1) - genType = ClassUtil.getGenericClasses(type, List.class); - List list = (List)type.newInstance(); + else if (Collection.class.isAssignableFrom(type)) { + if (genericType == null || genericType.length < 1) + genericType = ClassUtil.getGenericClasses(type, List.class); + Collection list = (Collection) type.getDeclaredConstructor().newInstance(); + for (int i=0; i=1? genType[0] : null), null, key, json.get(i))); + list.add(readType((genericType.length>=1? genericType[0] : null), null, key, json.get(i))); } return list; } else if (Map.class.isAssignableFrom(type)) { - if (genType == null || genType.length < 2) - genType = ClassUtil.getGenericClasses(type, Map.class); - Map map = (Map)type.newInstance(); + if (genericType == null || genericType.length < 2) + genericType = ClassUtil.getGenericClasses(type, Map.class); + Map map = (Map) type.getDeclaredConstructor().newInstance(); + for (Iterator it=json.keyIterator(); it.hasNext();) { String subKey = it.next(); if (json.get(subKey) != null) { map.put( subKey, - readType((genType.length >= 2 ? genType[1] : null), null, subKey, json.get(subKey))); + readType((genericType.length >= 2 ? genericType[1] : null), null, subKey, json.get(subKey))); } } return map; @@ -170,7 +171,7 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C } - protected Object readObject(Class type, String key, DataNode json) throws IllegalAccessException, InstantiationException, ClassNotFoundException, IllegalArgumentException, NoSuchFieldException { + protected Object readObject(Class type, String key, DataNode json) throws IllegalAccessException, InstantiationException, ClassNotFoundException, IllegalArgumentException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException { // Only parse if json is a map if (json == null || !json.isMap()) return null; @@ -182,17 +183,17 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C Object obj; // Try using explicit class if (type != null) { - obj = type.newInstance(); + obj = type.getDeclaredConstructor().newInstance(); } // Try using metadata else if (json.getString(MD_CLASS) != null) { Class objClass = Class.forName(json.getString(MD_CLASS)); - obj = objClass.newInstance(); + obj = objClass.getDeclaredConstructor().newInstance(); } // Search for registered classes else if (registeredClasses.containsKey(key)) { Class objClass = registeredClasses.get(key); - obj = objClass.newInstance(); + obj = objClass.getDeclaredConstructor().newInstance(); } // Unknown class else { diff --git a/src/zutil/parser/json/JSONObjectOutputStream.java b/src/zutil/parser/json/JSONObjectOutputStream.java index 6e99d73..e684511 100755 --- a/src/zutil/parser/json/JSONObjectOutputStream.java +++ b/src/zutil/parser/json/JSONObjectOutputStream.java @@ -34,8 +34,8 @@ import java.io.*; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; import static zutil.parser.json.JSONObjectInputStream.MD_CLASS; @@ -101,6 +101,9 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput ClassUtil.isWrapper(obj.getClass())) { root = getPrimitiveDataNode(obj.getClass(), obj); } + else if (obj.getClass().isEnum()) { + root = getPrimitiveDataNode(String.class, obj.toString()); + } // Add an array else if (objClass.isArray()) { // Special case for byte arrays @@ -117,9 +120,9 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput } } // List - else if (List.class.isAssignableFrom(objClass)) { + else if (Collection.class.isAssignableFrom(objClass)) { root = new DataNode(DataNode.DataType.List); - List list = (List)obj; + Collection list = (Collection) obj; for (Object item : list) { root.add(getDataNode(item)); } @@ -127,7 +130,7 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput // Map else if (Map.class.isAssignableFrom(objClass)) { root = new DataNode(DataNode.DataType.Map); - Map map = (Map)obj; + Map map = (Map) obj; for (Object key : map.keySet()) { root.set( getDataNode(key).getString(), @@ -170,7 +173,7 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput private DataNode getPrimitiveDataNode(Class type, Object value) throws IllegalArgumentException { DataNode node; - if (type == int.class || + if (type == int.class || type == Integer.class || type == long.class || type == Long.class || diff --git a/test/zutil/parser/json/JSONSerializerTest.java b/test/zutil/parser/json/JSONSerializerTest.java index 1fad7d8..7e62221 100755 --- a/test/zutil/parser/json/JSONSerializerTest.java +++ b/test/zutil/parser/json/JSONSerializerTest.java @@ -38,7 +38,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; -public class JSONSerializerTest{ +public class JSONSerializerTest { @Test public void testOutputSerializerWithPrimitives() throws IOException { @@ -50,6 +50,7 @@ public class JSONSerializerTest{ assertThat(data, containsString("'str': 'abcd'")); assertThat(data, containsString("'value': 42")); assertThat(data, containsString("'decimal': 1.1")); + assertThat(data, containsString("'testEnum': 'ENUM2'")); } @Test @@ -175,13 +176,13 @@ public class JSONSerializerTest{ /******************* Utility Functions ************************************/ - static T sendReceiveObject(T sourceObj) throws IOException{ + static T sendReceiveObject(T sourceObj) throws IOException { return readObjectFromJson( writeObjectToJson(sourceObj)); } @SuppressWarnings("unchecked") - static T readObjectFromJson(String json) throws IOException{ + static T readObjectFromJson(String json) throws IOException { StringReader bin = new StringReader(json); JSONObjectInputStream in = new JSONObjectInputStream(bin); T targetObj = (T) in.readObject(); @@ -190,10 +191,10 @@ public class JSONSerializerTest{ return targetObj; } - static String writeObjectToJson(T sourceObj) throws IOException{ + static String writeObjectToJson(T sourceObj) throws IOException { return writeObjectToJson(sourceObj, true); } - private static String writeObjectToJson(T sourceObj, boolean metadata) throws IOException{ + private static String writeObjectToJson(T sourceObj, boolean metadata) throws IOException { StringOutputStream bout = new StringOutputStream(); JSONObjectOutputStream out = new JSONObjectOutputStream(bout); out.enableMetaData(metadata); @@ -207,39 +208,46 @@ public class JSONSerializerTest{ /******************** Test Classes ************************************/ - public static class TestClass implements Serializable{ + public enum TestEnum { + ENUM1, ENUM2, ENUM3 + } + + public static class TestClass implements Serializable { private static final long serialVersionUID = 1L; String str; double decimal; TestObj obj1; TestObj obj2; + TestEnum testEnum; - public TestClass init(){ + public TestClass init() { this.str = "abcd"; this.decimal = 1.1; this.obj1 = new TestObj().init(); this.obj2 = new TestObj().init(); + this.testEnum = TestEnum.ENUM2; return this; } - public static void assertEquals(TestClass obj1, TestClass obj2){ + public static void assertEquals(TestClass obj1, TestClass obj2) { Assert.assertEquals(obj1.str, obj2.str); Assert.assertEquals(obj1.decimal, obj2.decimal, 0.001); Assert.assertEquals(obj1.obj1, obj2.obj1); Assert.assertEquals(obj1.obj2, obj2.obj2); + Assert.assertEquals(obj1.testEnum, obj2.testEnum); } } - public static class TestClassObjClone{ + public static class TestClassObjClone { TestObj obj1; TestObj obj2; - public TestClassObjClone init(){ + public TestClassObjClone init() { this.obj1 = this.obj2 = new TestObj().init(); return this; } - public boolean equals(Object obj){ + public boolean equals(Object obj) { return obj instanceof TestClassObjClone && this.obj1.equals(((TestClassObjClone)obj).obj1) && this.obj1 == this.obj2 && @@ -247,50 +255,50 @@ public class JSONSerializerTest{ } } - public static class TestObj implements Serializable{ + public static class TestObj implements Serializable { private static final long serialVersionUID = 1L; int value; - public TestObj init(){ + public TestObj init() { this.value = 42; return this; } - public boolean equals(Object obj){ + public boolean equals(Object obj) { return obj instanceof TestObj && this.value == ((TestObj)obj).value; } } - public static class TestClassArray{ + public static class TestClassArray { private int[] array; - public TestClassArray init(){ + public TestClassArray init() { array = new int[]{1,2,3,4}; return this; } - public boolean equals(Object obj){ + public boolean equals(Object obj) { return obj instanceof TestClassArray && Arrays.equals(this.array ,((TestClassArray)obj).array); } } - public static class TestClassStringArray{ + public static class TestClassStringArray { private String[] array; - public TestClassStringArray init(){ + public TestClassStringArray init() { array = new String[]{"test","string","array"}; return this; } - public boolean equals(Object obj){ + public boolean equals(Object obj) { return obj instanceof TestClassStringArray && Arrays.equals(this.array ,((TestClassStringArray)obj).array); } } - public static class TestClassMap{ + public static class TestClassMap { private HashMap map; public TestClassMap init(){ @@ -300,22 +308,22 @@ public class JSONSerializerTest{ return this; } - public static void assertEquals(TestClassMap obj1, TestClassMap obj2){ + public static void assertEquals(TestClassMap obj1, TestClassMap obj2) { Assert.assertEquals(obj1.map, obj2.map); } } - public static class TestClassList{ + public static class TestClassList { private ArrayList list; - public TestClassList init(){ + public TestClassList init() { list = new ArrayList<>(); list.add("value1"); list.add("value2"); return this; } - public static void assertEquals(TestClassList obj1, TestClassList obj2){ + public static void assertEquals(TestClassList obj1, TestClassList obj2) { Assert.assertEquals(obj1.list, obj2.list); } }