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) {
// 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)) {

View file

@ -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<String, Class> registeredClasses;
private HashMap<Integer, Object> objectCache;
private HashMap<String, Class> registeredClasses = new HashMap<>();
private HashMap<Integer, Object> 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<? extends Enum>)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<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;
}
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<String> 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 {

View file

@ -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 ||

View file

@ -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> T sendReceiveObject(T sourceObj) throws IOException{
static <T> T sendReceiveObject(T sourceObj) throws IOException {
return readObjectFromJson(
writeObjectToJson(sourceObj));
}
@SuppressWarnings("unchecked")
static <T> T readObjectFromJson(String json) throws IOException{
static <T> 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 <T> String writeObjectToJson(T sourceObj) throws IOException{
static <T> String writeObjectToJson(T sourceObj) throws IOException {
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();
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<String,String> 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<String> 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);
}
}