Created
January 9, 2016 19:16
-
-
Save chRyNaN/f300d18b0342d18d951c to your computer and use it in GitHub Desktop.
Simple JSONObject Serialization
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.json.JSONObject; | |
/** | |
* Created by chRyNaN on 1/9/2016. Java SE 8 compatible Interface. Simply implement this interface in your custom | |
* class and your Object is able to be converted to and from a JSONObject. | |
*/ | |
public interface JSONable { | |
default JSONObject toJSON(){ | |
return JSONSerializer.toJSON(this); | |
} | |
default String toJSONString(){ | |
JSONObject obj = this.toJSON(); | |
if(obj != null){ | |
return obj.toString(); | |
} | |
return this.toString(); | |
} | |
default void fromJSON(JSONObject obj){ | |
JSONSerializer.fromJSON(obj, this); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.json.JSONObject; | |
/** | |
* Created by chRyNaN on 1/9/2016. I Wanted this to be an interface that has default methods, that way | |
* you could simply implement the interface in your class without having to provide code or being stuck | |
* in a single hierarchy. Unfortunately, Android doesn't support default methods (or many features of Java SE 8). | |
* If you would like to use this in a Java SE 8 environment, it would be better to change this to an interface | |
* (replace "abstract class" with "interface") and add the "default" qualifier to the beginning of the methods. | |
*/ | |
public abstract class JSONableObject { | |
JSONObject toJSON(){ | |
return JSONSerializer.toJSON(this); | |
} | |
String toJSONString(){ | |
JSONObject obj = this.toJSON(); | |
if(obj != null){ | |
return obj.toString(); | |
} | |
return this.toString(); | |
} | |
void fromJSON(JSONObject obj){ | |
JSONSerializer.fromJSON(obj, this); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.json.JSONArray; | |
import org.json.JSONException; | |
import org.json.JSONObject; | |
import java.lang.reflect.Array; | |
import java.lang.reflect.Field; | |
import java.util.ArrayDeque; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Deque; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.PriorityQueue; | |
import java.util.Queue; | |
import java.util.Set; | |
/** | |
* Created by chRyNaN on 1/9/2016. Turns a JSONObject into an Object and an Object into a JSONObject. | |
* Untested as of now and doesn't work for some specific and more complex use cases. | |
*/ | |
public class JSONSerializer { | |
public static <T> JSONObject toJSON(T obj){ | |
JSONObject json = new JSONObject(); | |
JSONArray jArray = new JSONArray(); | |
JSONObject o; | |
Class<T> clazz = (Class<T>) obj.getClass(); | |
for (Field f : clazz.getDeclaredFields()) { | |
try { | |
f.setAccessible(true); | |
Object property = f.get(obj); | |
if (property instanceof Map<?, ?>) { | |
//Create JSONArray of JSONObjects | |
for(Map.Entry<?, ?> e : ((Map<?, ?>) property).entrySet()){ | |
o = new JSONObject(); | |
o.put("key", e.getKey()); | |
o.put("value", e.getValue()); | |
jArray.put(o); | |
} | |
json.put(f.getName(), jArray); | |
}else if (property instanceof Collection<?>){ | |
for(Iterator<?> i = ((Collection<?>) property).iterator(); i.hasNext();){ | |
jArray.put(i.next()); | |
} | |
json.put(f.getName(), jArray); | |
}else if (property.getClass().isArray()){ | |
Object[] oArray = toUseableArray(property); | |
for(int i = 0; i < oArray.length; i++){ | |
jArray.put(oArray[i]); | |
} | |
json.put(f.getName(), jArray); | |
}else { | |
json.put(f.getName(), property); | |
} | |
} catch (NullPointerException npe) { | |
npe.printStackTrace(); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
return json; | |
} | |
public static <T> T fromJSON(JSONObject obj, T entity){ | |
if(obj != null){ | |
Class<T> clazz = (Class<T>) entity.getClass(); | |
JSONArray jArray; | |
JSONObject o; | |
for(Iterator<String> it = obj.keys(); it.hasNext();){ | |
try { | |
String s = it.next(); | |
Field f = clazz.getDeclaredField(s); | |
f.setAccessible(true); | |
Class<?> type = f.getType(); | |
if(type.isAssignableFrom(Map.class)){ | |
Map<Object, Object> map = new HashMap<>(); | |
jArray = obj.getJSONArray(s); | |
for(int i = 0; i < jArray.length(); i++){ | |
o = jArray.getJSONObject(i); | |
map.put(o.get("key"), o.get("value")); | |
} | |
f.set(entity, map); | |
}else if(type.isAssignableFrom(Collection.class)){ | |
Collection<Object> collection = getCollection(type); | |
jArray = obj.getJSONArray(s); | |
for(int i = 0; i < jArray.length(); i++){ | |
collection.add(jArray.get(i)); | |
} | |
f.set(entity, collection); | |
}else if(type.isArray()){ | |
//TODO uh-oh what about unboxing back into primitive types? | |
jArray = obj.getJSONArray(s); | |
Object[] array = new Object[s.length()]; | |
for(int i = 0; i < jArray.length(); i++){ | |
array[i] = jArray.get(i); | |
} | |
f.set(entity, array); | |
}else{ | |
f.set(entity, obj.get(s)); | |
} | |
} catch (NoSuchFieldException ne) { | |
ne.printStackTrace(); | |
} catch (JSONException je){ | |
je.printStackTrace(); | |
} catch (Exception e){ | |
e.printStackTrace(); | |
} | |
} | |
} | |
return entity; | |
} | |
private static Collection<Object> getCollection(Class<?> type){ | |
Collection<Object> c = new ArrayList<>(); | |
try{ | |
if(type.isAssignableFrom(List.class)){ | |
c = new ArrayList<>(); | |
}else if(type.isAssignableFrom(Set.class)){ | |
c = new HashSet<>(); | |
}else if(type.isAssignableFrom(Queue.class)){ | |
c = new PriorityQueue<>(); | |
}else if(type.isAssignableFrom(Deque.class)){ | |
c = new ArrayDeque<>(); | |
} | |
}catch(Exception e){ | |
e.printStackTrace(); | |
} | |
return c; | |
} | |
/* | |
* If you have an Object (of unknown type) which is an array, you can simply iterate through the array after | |
* casting it to an array object (Object[]), but this ignores primitive types. Primitive types must first | |
* be boxed to their corresponding objects types. | |
*/ | |
private static Object[] toUseableArray(Object obj){ | |
//make sure the object is an array | |
if(obj.getClass().isArray()){ | |
if(obj instanceof int[]){ | |
return toBoxedArray(obj); | |
}else if(obj instanceof double[]){ | |
return toBoxedArray(obj); | |
}else if(obj instanceof float[]){ | |
return toBoxedArray(obj); | |
}else if(obj instanceof short[]){ | |
return toBoxedArray(obj); | |
}else if(obj instanceof char[]){ | |
return toBoxedArray(obj); | |
}else if(obj instanceof byte[]){ | |
return toBoxedArray(obj); | |
}else if(obj instanceof long[]){ | |
return toBoxedArray(obj); | |
}else if(obj instanceof boolean[]){ | |
return toBoxedArray(obj); | |
}else{ | |
//already is an Object array so cast and return it | |
return (Object[]) obj; | |
} | |
} | |
return null; | |
} | |
/* | |
* Generic method to box primitive arrays to their Object counter parts. | |
* Referenced this answer on StackOverflow: http://stackoverflow.com/a/3775583/1478764 | |
* Return type must be known when calling this method. | |
*/ | |
private static <T> T[] toBoxedArray(Object obj){ | |
if(obj == null) { | |
throw new NullPointerException("Null values are not supported"); | |
} | |
final Class<?> cls = obj.getClass(); | |
if(!cls.isArray() || !cls.getComponentType().isPrimitive()) { | |
throw new IllegalArgumentException("Only primitive arrays are supported"); | |
} | |
final int length = Array.getLength(obj); | |
if(length==0) { | |
throw new IllegalArgumentException("Only non-empty primitive arrays are supported"); | |
} | |
T[] arr = null; | |
for (int i = 0; i < length; i++) { | |
final Object wrapped = Array.get(obj, i); | |
if(arr == null){ | |
arr = (T[]) Array.newInstance(wrapped.getClass(), length); | |
} | |
arr[i] = (T) wrapped; | |
} | |
return arr; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment