Added Map and List support to JSON object stream
This commit is contained in:
parent
0dd8e7b415
commit
4a817cbf31
6 changed files with 139 additions and 50 deletions
|
|
@ -24,6 +24,10 @@
|
||||||
|
|
||||||
package zutil;
|
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;
|
import java.util.HashSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -77,4 +81,17 @@ public class ClassUtil {
|
||||||
public static boolean isPrimitive(Class<?> type){
|
public static boolean isPrimitive(Class<?> type){
|
||||||
return primitives.contains( 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import java.util.*;
|
||||||
*/
|
*/
|
||||||
public class DataNode implements Iterable<DataNode>{
|
public class DataNode implements Iterable<DataNode>{
|
||||||
public enum DataType{
|
public enum DataType{
|
||||||
Map, List, String, Number, Boolean, Null
|
Map, List, String, Number, Boolean
|
||||||
}
|
}
|
||||||
private Map<String,DataNode> map = null;
|
private Map<String,DataNode> map = null;
|
||||||
private List<DataNode> list = null;
|
private List<DataNode> list = null;
|
||||||
|
|
@ -249,9 +249,6 @@ public class DataNode implements Iterable<DataNode>{
|
||||||
public boolean isValue(){
|
public boolean isValue(){
|
||||||
return type != DataType.Map && type != DataType.List;
|
return type != DataType.Map && type != DataType.List;
|
||||||
}
|
}
|
||||||
public boolean isNull(){
|
|
||||||
return type == DataType.Null;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @return the type of the node
|
* @return the type of the node
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
package zutil.parser.json;
|
package zutil.parser.json;
|
||||||
|
|
||||||
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
|
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
|
||||||
|
import zutil.ClassUtil;
|
||||||
import zutil.log.LogUtil;
|
import zutil.log.LogUtil;
|
||||||
import zutil.parser.Base64Decoder;
|
import zutil.parser.Base64Decoder;
|
||||||
import zutil.parser.DataNode;
|
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<json.size(); i++){
|
||||||
|
Array.set(array, i, readType(type.getComponentType(), null, key, json.get(i)));
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(List.class.isAssignableFrom(type)){
|
||||||
|
List list = (List)type.newInstance();
|
||||||
|
for(int i=0; i<json.size(); i++){
|
||||||
|
list.add(readType(genType[0], null, key, json.get(i)));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
else if(Map.class.isAssignableFrom(type)){
|
||||||
|
Map map = (Map)type.newInstance();
|
||||||
|
for(Iterator<String> 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 {
|
protected Object readObject(Class<?> type, String key, DataNode json) throws IllegalAccessException, InstantiationException, ClassNotFoundException, IllegalArgumentException, UnsupportedDataTypeException, NoSuchFieldException {
|
||||||
// Only parse if json is a map
|
// Only parse if json is a map
|
||||||
if(!json.isMap())
|
if(!json.isMap())
|
||||||
|
|
@ -158,8 +200,9 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
|
||||||
json.get(field.getName()) != null){
|
json.get(field.getName()) != null){
|
||||||
// Parse field
|
// Parse field
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(obj, readField(
|
field.set(obj, readType(
|
||||||
field.getType(),
|
field.getType(),
|
||||||
|
ClassUtil.getGenericClasses(field),
|
||||||
field.getName(),
|
field.getName(),
|
||||||
json.get(field.getName())));
|
json.get(field.getName())));
|
||||||
}
|
}
|
||||||
|
|
@ -170,46 +213,6 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
|
||||||
return obj;
|
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<json.size(); i++){
|
|
||||||
Array.set(array, i, readField(type.getComponentType(), key, json.get(i)));
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(List.class.isAssignableFrom(type)){
|
|
||||||
List list = (List)type.newInstance();
|
|
||||||
for(int i=0; i<json.size(); i++){
|
|
||||||
list.add(readObject(null, key, json.get(i)));
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
else if(Map.class.isAssignableFrom(type)){
|
|
||||||
Map map = (Map)type.newInstance();
|
|
||||||
for(Iterator<String> 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){
|
protected static Object readPrimitive(Class<?> type, DataNode json){
|
||||||
if (type == int.class ||
|
if (type == int.class ||
|
||||||
|
|
|
||||||
|
|
@ -123,10 +123,22 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput
|
||||||
root.set(field.getName(), arrayNode);
|
root.set(field.getName(), arrayNode);
|
||||||
}
|
}
|
||||||
else if(List.class.isAssignableFrom(field.getType())){
|
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())){
|
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{
|
else{
|
||||||
root.set(field.getName(), getDataNode(fieldObj));
|
root.set(field.getName(), getDataNode(fieldObj));
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ public class JSONParser extends Parser {
|
||||||
// Check what type of type the data is
|
// Check what type of type the data is
|
||||||
String data = tmp.toString().trim();
|
String data = tmp.toString().trim();
|
||||||
if( NULL_PATTERN.matcher(data).matches() )
|
if( NULL_PATTERN.matcher(data).matches() )
|
||||||
root = new DataNode(DataType.Null);
|
root = null;
|
||||||
else if( BOOLEAN_PATTERN.matcher(data).matches() )
|
else if( BOOLEAN_PATTERN.matcher(data).matches() )
|
||||||
root = new DataNode(DataType.Boolean);
|
root = new DataNode(DataType.Boolean);
|
||||||
else if( NUMBER_PATTERN.matcher(data).matches() )
|
else if( NUMBER_PATTERN.matcher(data).matches() )
|
||||||
|
|
@ -149,6 +149,7 @@ public class JSONParser extends Parser {
|
||||||
root = new DataNode(DataType.String);
|
root = new DataNode(DataType.String);
|
||||||
data = unEscapeString(data);
|
data = unEscapeString(data);
|
||||||
}
|
}
|
||||||
|
if(root != null)
|
||||||
root.set(data);
|
root.set(data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@ import zutil.parser.json.JSONObjectOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
|
@ -124,6 +126,33 @@ public class JSONSerializerTest{
|
||||||
TestClass.assertEquals(sourceObj, targetObj);
|
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 ************************************/
|
/******************* Utility Functions ************************************/
|
||||||
|
|
||||||
|
|
@ -155,7 +184,7 @@ public class JSONSerializerTest{
|
||||||
out.close();
|
out.close();
|
||||||
|
|
||||||
String data = bout.toString();
|
String data = bout.toString();
|
||||||
//System.out.println(data);
|
System.out.println(data);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
@ -244,4 +273,34 @@ public class JSONSerializerTest{
|
||||||
Arrays.equals(this.array ,((TestClassStringArray)obj).array);
|
Arrays.equals(this.array ,((TestClassStringArray)obj).array);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class TestClassMap{
|
||||||
|
private HashMap<String,String> 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<String> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue