Skip to content

Instantly share code, notes, and snippets.

@chRyNaN
Created January 9, 2016 19:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chRyNaN/f300d18b0342d18d951c to your computer and use it in GitHub Desktop.
Save chRyNaN/f300d18b0342d18d951c to your computer and use it in GitHub Desktop.
Simple JSONObject Serialization
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);
}
}
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);
}
}
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