Added support for Enum and HasSets to JSON serializer

This commit is contained in:
Ziver Koc 2021-07-12 22:39:36 +02:00
parent f4f52e997b
commit 67483895c3
4 changed files with 71 additions and 59 deletions

View file

@ -149,7 +149,7 @@ public class DBBeanSQLResultHandler<T> implements SQLResultHandler<T> {
if (obj == null) { if (obj == null) {
// Cache miss create a new bean // Cache miss create a new bean
logger.fine("Creating new Bean(" + beanClass.getName() + ") with id: " + id); logger.fine("Creating new Bean(" + beanClass.getName() + ") with id: " + id);
obj = beanClass.newInstance(); obj = beanClass.getDeclaredConstructor().newInstance();
obj.setId(id); obj.setId(id);
updateBean(result, obj); updateBean(result, obj);
} else if (DBBeanCache.isOutDated(obj)) { } else if (DBBeanCache.isOutDated(obj)) {

View file

@ -32,11 +32,9 @@ import zutil.parser.DataNode;
import java.io.*; import java.io.*;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.HashMap; import java.util.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -47,20 +45,15 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
protected static final String MD_CLASS = "@class"; protected static final String MD_CLASS = "@class";
private JSONParser parser; private JSONParser parser;
private HashMap<String, Class> registeredClasses; private HashMap<String, Class> registeredClasses = new HashMap<>();
private HashMap<Integer, Object> objectCache; private HashMap<Integer, Object> objectCache = new HashMap<>();
private JSONObjectInputStream() { private JSONObjectInputStream() {}
this.registeredClasses = new HashMap<>();
this.objectCache = new HashMap<>();
}
public JSONObjectInputStream(Reader in) { public JSONObjectInputStream(Reader in) {
this();
this.parser = new JSONParser(in); this.parser = new JSONParser(in);
} }
public JSONObjectInputStream(InputStream in) { public JSONObjectInputStream(InputStream in) {
this();
this.parser = new JSONParser(in); this.parser = new JSONParser(in);
} }
@ -110,7 +103,7 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
try { try {
DataNode root = parser.read(); DataNode root = parser.read();
if (root != null) { if (root != null) {
return (T)readObject(c, null, root); return (T) readObject(c, null, root);
} }
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.WARNING, null, e); logger.log(Level.WARNING, null, e);
@ -122,13 +115,19 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
@SuppressWarnings({ "rawtypes", "unchecked" }) @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) if (json == null || type == null)
return null; return null;
// Field type is a primitive? // Field type is a primitive?
if (type.isPrimitive() || String.class.isAssignableFrom(type)) { if (ClassUtil.isPrimitive(type) ||
ClassUtil.isWrapper(type)) {
return readPrimitive(type, json); return readPrimitive(type, json);
} }
else if (type.isEnum()) {
return Enum.valueOf((Class<? extends Enum>)type, (String) readPrimitive(String.class, json));
}
else if (type.isArray()) { else if (type.isArray()) {
if (type.getComponentType() == byte.class) if (type.getComponentType() == byte.class)
return Base64Decoder.decodeToByte(json.getString()); return Base64Decoder.decodeToByte(json.getString());
@ -140,25 +139,27 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
return array; return array;
} }
} }
else if (List.class.isAssignableFrom(type)) { else if (Collection.class.isAssignableFrom(type)) {
if (genType == null || genType.length < 1) if (genericType == null || genericType.length < 1)
genType = ClassUtil.getGenericClasses(type, List.class); genericType = ClassUtil.getGenericClasses(type, List.class);
List list = (List)type.newInstance(); Collection list = (Collection) type.getDeclaredConstructor().newInstance();
for (int i=0; i<json.size(); i++) { for (int i=0; i<json.size(); i++) {
list.add(readType((genType.length>=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; return list;
} }
else if (Map.class.isAssignableFrom(type)) { else if (Map.class.isAssignableFrom(type)) {
if (genType == null || genType.length < 2) if (genericType == null || genericType.length < 2)
genType = ClassUtil.getGenericClasses(type, Map.class); genericType = ClassUtil.getGenericClasses(type, Map.class);
Map map = (Map)type.newInstance(); Map map = (Map) type.getDeclaredConstructor().newInstance();
for (Iterator<String> it=json.keyIterator(); it.hasNext();) { for (Iterator<String> it=json.keyIterator(); it.hasNext();) {
String subKey = it.next(); String subKey = it.next();
if (json.get(subKey) != null) { if (json.get(subKey) != null) {
map.put( map.put(
subKey, 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; 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 // Only parse if json is a map
if (json == null || !json.isMap()) if (json == null || !json.isMap())
return null; return null;
@ -182,17 +183,17 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
Object obj; Object obj;
// Try using explicit class // Try using explicit class
if (type != null) { if (type != null) {
obj = type.newInstance(); obj = type.getDeclaredConstructor().newInstance();
} }
// Try using metadata // Try using metadata
else if (json.getString(MD_CLASS) != null) { else if (json.getString(MD_CLASS) != null) {
Class<?> objClass = Class.forName(json.getString(MD_CLASS)); Class<?> objClass = Class.forName(json.getString(MD_CLASS));
obj = objClass.newInstance(); obj = objClass.getDeclaredConstructor().newInstance();
} }
// Search for registered classes // Search for registered classes
else if (registeredClasses.containsKey(key)) { else if (registeredClasses.containsKey(key)) {
Class<?> objClass = registeredClasses.get(key); Class<?> objClass = registeredClasses.get(key);
obj = objClass.newInstance(); obj = objClass.getDeclaredConstructor().newInstance();
} }
// Unknown class // Unknown class
else { else {

View file

@ -34,8 +34,8 @@ import java.io.*;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import static zutil.parser.json.JSONObjectInputStream.MD_CLASS; import static zutil.parser.json.JSONObjectInputStream.MD_CLASS;
@ -101,6 +101,9 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput
ClassUtil.isWrapper(obj.getClass())) { ClassUtil.isWrapper(obj.getClass())) {
root = getPrimitiveDataNode(obj.getClass(), obj); root = getPrimitiveDataNode(obj.getClass(), obj);
} }
else if (obj.getClass().isEnum()) {
root = getPrimitiveDataNode(String.class, obj.toString());
}
// Add an array // Add an array
else if (objClass.isArray()) { else if (objClass.isArray()) {
// Special case for byte arrays // Special case for byte arrays
@ -117,9 +120,9 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput
} }
} }
// List // List
else if (List.class.isAssignableFrom(objClass)) { else if (Collection.class.isAssignableFrom(objClass)) {
root = new DataNode(DataNode.DataType.List); root = new DataNode(DataNode.DataType.List);
List list = (List)obj; Collection<?> list = (Collection<?>) obj;
for (Object item : list) { for (Object item : list) {
root.add(getDataNode(item)); root.add(getDataNode(item));
} }
@ -127,7 +130,7 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput
// Map // Map
else if (Map.class.isAssignableFrom(objClass)) { else if (Map.class.isAssignableFrom(objClass)) {
root = new DataNode(DataNode.DataType.Map); root = new DataNode(DataNode.DataType.Map);
Map map = (Map)obj; Map<?, ?> map = (Map<?, ?>) obj;
for (Object key : map.keySet()) { for (Object key : map.keySet()) {
root.set( root.set(
getDataNode(key).getString(), getDataNode(key).getString(),

View file

@ -38,7 +38,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
public class JSONSerializerTest{ public class JSONSerializerTest {
@Test @Test
public void testOutputSerializerWithPrimitives() throws IOException { public void testOutputSerializerWithPrimitives() throws IOException {
@ -50,6 +50,7 @@ public class JSONSerializerTest{
assertThat(data, containsString("'str': 'abcd'")); assertThat(data, containsString("'str': 'abcd'"));
assertThat(data, containsString("'value': 42")); assertThat(data, containsString("'value': 42"));
assertThat(data, containsString("'decimal': 1.1")); assertThat(data, containsString("'decimal': 1.1"));
assertThat(data, containsString("'testEnum': 'ENUM2'"));
} }
@Test @Test
@ -175,13 +176,13 @@ public class JSONSerializerTest{
/******************* Utility Functions ************************************/ /******************* Utility Functions ************************************/
static <T> T sendReceiveObject(T sourceObj) throws IOException{ static <T> T sendReceiveObject(T sourceObj) throws IOException {
return readObjectFromJson( return readObjectFromJson(
writeObjectToJson(sourceObj)); writeObjectToJson(sourceObj));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T> T readObjectFromJson(String json) throws IOException{ static <T> T readObjectFromJson(String json) throws IOException {
StringReader bin = new StringReader(json); StringReader bin = new StringReader(json);
JSONObjectInputStream in = new JSONObjectInputStream(bin); JSONObjectInputStream in = new JSONObjectInputStream(bin);
T targetObj = (T) in.readObject(); T targetObj = (T) in.readObject();
@ -190,10 +191,10 @@ public class JSONSerializerTest{
return targetObj; return targetObj;
} }
static <T> String writeObjectToJson(T sourceObj) throws IOException{ static <T> String writeObjectToJson(T sourceObj) throws IOException {
return writeObjectToJson(sourceObj, true); return writeObjectToJson(sourceObj, true);
} }
private static <T> String writeObjectToJson(T sourceObj, boolean metadata) throws IOException{ private static <T> String writeObjectToJson(T sourceObj, boolean metadata) throws IOException {
StringOutputStream bout = new StringOutputStream(); StringOutputStream bout = new StringOutputStream();
JSONObjectOutputStream out = new JSONObjectOutputStream(bout); JSONObjectOutputStream out = new JSONObjectOutputStream(bout);
out.enableMetaData(metadata); out.enableMetaData(metadata);
@ -207,39 +208,46 @@ public class JSONSerializerTest{
/******************** Test Classes ************************************/ /******************** 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; private static final long serialVersionUID = 1L;
String str; String str;
double decimal; double decimal;
TestObj obj1; TestObj obj1;
TestObj obj2; TestObj obj2;
TestEnum testEnum;
public TestClass init(){ public TestClass init() {
this.str = "abcd"; this.str = "abcd";
this.decimal = 1.1; this.decimal = 1.1;
this.obj1 = new TestObj().init(); this.obj1 = new TestObj().init();
this.obj2 = new TestObj().init(); this.obj2 = new TestObj().init();
this.testEnum = TestEnum.ENUM2;
return this; 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.str, obj2.str);
Assert.assertEquals(obj1.decimal, obj2.decimal, 0.001); Assert.assertEquals(obj1.decimal, obj2.decimal, 0.001);
Assert.assertEquals(obj1.obj1, obj2.obj1); Assert.assertEquals(obj1.obj1, obj2.obj1);
Assert.assertEquals(obj1.obj2, obj2.obj2); Assert.assertEquals(obj1.obj2, obj2.obj2);
Assert.assertEquals(obj1.testEnum, obj2.testEnum);
} }
} }
public static class TestClassObjClone{ public static class TestClassObjClone {
TestObj obj1; TestObj obj1;
TestObj obj2; TestObj obj2;
public TestClassObjClone init(){ public TestClassObjClone init() {
this.obj1 = this.obj2 = new TestObj().init(); this.obj1 = this.obj2 = new TestObj().init();
return this; return this;
} }
public boolean equals(Object obj){ public boolean equals(Object obj) {
return obj instanceof TestClassObjClone && return obj instanceof TestClassObjClone &&
this.obj1.equals(((TestClassObjClone)obj).obj1) && this.obj1.equals(((TestClassObjClone)obj).obj1) &&
this.obj1 == this.obj2 && 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; private static final long serialVersionUID = 1L;
int value; int value;
public TestObj init(){ public TestObj init() {
this.value = 42; this.value = 42;
return this; return this;
} }
public boolean equals(Object obj){ public boolean equals(Object obj) {
return obj instanceof TestObj && return obj instanceof TestObj &&
this.value == ((TestObj)obj).value; this.value == ((TestObj)obj).value;
} }
} }
public static class TestClassArray{ public static class TestClassArray {
private int[] array; private int[] array;
public TestClassArray init(){ public TestClassArray init() {
array = new int[]{1,2,3,4}; array = new int[]{1,2,3,4};
return this; return this;
} }
public boolean equals(Object obj){ public boolean equals(Object obj) {
return obj instanceof TestClassArray && return obj instanceof TestClassArray &&
Arrays.equals(this.array ,((TestClassArray)obj).array); Arrays.equals(this.array ,((TestClassArray)obj).array);
} }
} }
public static class TestClassStringArray{ public static class TestClassStringArray {
private String[] array; private String[] array;
public TestClassStringArray init(){ public TestClassStringArray init() {
array = new String[]{"test","string","array"}; array = new String[]{"test","string","array"};
return this; return this;
} }
public boolean equals(Object obj){ public boolean equals(Object obj) {
return obj instanceof TestClassStringArray && return obj instanceof TestClassStringArray &&
Arrays.equals(this.array ,((TestClassStringArray)obj).array); Arrays.equals(this.array ,((TestClassStringArray)obj).array);
} }
} }
public static class TestClassMap{ public static class TestClassMap {
private HashMap<String,String> map; private HashMap<String,String> map;
public TestClassMap init(){ public TestClassMap init(){
@ -300,22 +308,22 @@ public class JSONSerializerTest{
return this; return this;
} }
public static void assertEquals(TestClassMap obj1, TestClassMap obj2){ public static void assertEquals(TestClassMap obj1, TestClassMap obj2) {
Assert.assertEquals(obj1.map, obj2.map); Assert.assertEquals(obj1.map, obj2.map);
} }
} }
public static class TestClassList{ public static class TestClassList {
private ArrayList<String> list; private ArrayList<String> list;
public TestClassList init(){ public TestClassList init() {
list = new ArrayList<>(); list = new ArrayList<>();
list.add("value1"); list.add("value1");
list.add("value2"); list.add("value2");
return this; return this;
} }
public static void assertEquals(TestClassList obj1, TestClassList obj2){ public static void assertEquals(TestClassList obj1, TestClassList obj2) {
Assert.assertEquals(obj1.list, obj2.list); Assert.assertEquals(obj1.list, obj2.list);
} }
} }