package siena.embed;
import static siena.Json.list;
import static siena.Json.map;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import siena.Json;
import siena.Query;
import siena.SienaException;
import siena.Util;
public class JsonSerializer {
public static Json serialize(Object obj) {
return serialize(obj, null);
}
public static Json serialize(Object obj, Field f) {
if(obj == null) return new Json(null);
Class<?> clazz = obj.getClass();
//if(obj instanceof Map<?, ?>) {
if(Map.class.isAssignableFrom(clazz)){
Map<?, ?> map = (Map<?, ?>) obj;
Json result = map();
for (Map.Entry<?, ?> entry : map.entrySet()) {
String key = entry.getKey().toString();
Json value = serialize(entry.getValue(), null);
result.put(key, value);
}
return result;
}
//if(obj instanceof Collection<?>) {
if(Collection.class.isAssignableFrom(clazz)){
Json result = list();
Collection<?> col = (Collection<?>) obj;
for (Object object : col) {
result.add(serialize(object));
}
return result;
}
if(Json.class.isAssignableFrom(clazz)){
return new Json(obj);
}
if(Field.class.isAssignableFrom(clazz)){
return new Json(obj);
}
if(clazz.isArray()){
return new Json(obj);
}
if(clazz == Class.class){
return new Json(obj);
}
if(JsonDumpable.class.isAssignableFrom(obj.getClass())) {
return ((JsonDumpable)obj).dump();
}
try {
EmbeddedList list = obj.getClass().getAnnotation(EmbeddedList.class);
if(list != null) {
return serializeList(obj);
}
EmbeddedMap map = obj.getClass().getAnnotation(EmbeddedMap.class);
if(map != null) {
return serializeMap(obj);
}
} catch(SienaException e) {
throw e;
} catch(Exception e) {
throw new SienaException(e);
}
if(f != null) {
Format format = f.getAnnotation(Format.class);
if(format != null) {
if(obj.getClass() == Date.class) {
Date date = (Date) obj;
SimpleDateFormat sdf = new SimpleDateFormat(format.value());
return new Json(sdf.format(date));
}
}
}
return new Json(obj);
}
public static Json serializeMap(Object obj) throws Exception {
Field[] fields = obj.getClass().getDeclaredFields();
Json result = map();
for (Field f : fields) {
if(mustIgnore(f)) continue;
Key k = f.getAnnotation(Key.class);
if(k != null) {
result.put(k.value(), serialize(f.get(obj), f));
} else {
result.put(f.getName(), serialize(f.get(obj), f));
}
}
// TEST
// serializes super classes
Class<?> clazz = obj.getClass().getSuperclass();
while(clazz!=null){
fields = clazz.getDeclaredFields();
for (Field f : fields) {
if(mustIgnore(f)) continue;
Key k = f.getAnnotation(Key.class);
if(k != null) {
result.put(k.value(), serialize(Util.readField(obj, f), f));
} else {
result.put(f.getName(), serialize(Util.readField(obj, f), f));
}
}
clazz = clazz.getSuperclass();
}
// TEST
return result;
}
public static Json serializeList(Object obj) throws Exception {
Field[] fields = obj.getClass().getDeclaredFields();
Json result = list();
for (Field f : fields) {
if(mustIgnore(f)) continue;
At at = f.getAnnotation(At.class);
if(at == null) throw new SienaException("Field "+obj.getClass()+"."+f.getName()+" must be annotated with @At(n)");
result.addAt(at.value(), serialize(f.get(obj), f));
}
// TEST
// serializes super classes
Class<?> clazz = obj.getClass().getSuperclass();
while(clazz!=null){
fields = clazz.getDeclaredFields();
for (Field f : fields) {
if(mustIgnore(f)) continue;
At at = f.getAnnotation(At.class);
if(at == null) throw new SienaException("Field "+obj.getClass()+"."+f.getName()+" must be annotated with @At(n)");
result.addAt(at.value(), serialize(f.get(obj), f));
}
clazz = clazz.getSuperclass();
}
// TEST
return result;
}
public static Object deserializeMap(Class<?> clazz, Json data) {
if(!data.isMap()) {
throw new SienaException("Error while deserializating class "+clazz
+". A Json map is needed but found: "+data);
}
Object obj = Util.createObjectInstance(clazz);
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
if(mustIgnore(f)) continue;
Key key = f.getAnnotation(Key.class);
if(key != null)
Util.setField(obj, f, deserialize(f, data.get(key.value())));
else
Util.setField(obj, f, deserialize(f, data.get(f.getName())));
}
// deserializes super classes
Class<?> superclazz = obj.getClass().getSuperclass();
while(superclazz!=null){
fields = superclazz.getDeclaredFields();
for (Field f : fields) {
if(mustIgnore(f)) continue;
Key key = f.getAnnotation(Key.class);
if(key != null)
Util.setField(obj, f, deserialize(f, data.get(key.value())));
else
Util.setField(obj, f, deserialize(f, data.get(f.getName())));
}
superclazz = superclazz.getSuperclass();
}
return obj;
}
public static Object deserialize(Class<?> clazz, Json data) {
try {
EmbeddedMap map = clazz.getAnnotation(EmbeddedMap.class);
if(map != null) {
if(!data.isMap()) {
throw new SienaException("Error while deserializating class "+clazz
+". A Json map is needed but found: "+data);
}
Object obj = Util.createObjectInstance(clazz);
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
if(mustIgnore(f)) continue;
Key key = f.getAnnotation(Key.class);
if(key != null)
Util.setField(obj, f, deserialize(f, data.get(key.value())));
else
Util.setField(obj, f, deserialize(f, data.get(f.getName())));
}
// deserializes super classes
Class<?> superclazz = obj.getClass().getSuperclass();
while(superclazz!=null){
fields = superclazz.getDeclaredFields();
for (Field f : fields) {
if(mustIgnore(f)) continue;
Key key = f.getAnnotation(Key.class);
if(key != null)
Util.setField(obj, f, deserialize(f, data.get(key.value())));
else
Util.setField(obj, f, deserialize(f, data.get(f.getName())));
}
superclazz = superclazz.getSuperclass();
}
return obj;
}
EmbeddedList list = clazz.getAnnotation(EmbeddedList.class);
if(list != null) {
if(!data.isList()) {
throw new SienaException("Error while deserializating class "+clazz
+". A Json list is needed but found: "+data);
}
Object obj = Util.createObjectInstance(clazz);
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
if(mustIgnore(f)) continue;
At at = f.getAnnotation(At.class);
if(at == null) throw new SienaException("Field "+obj.getClass()+"."+f.getName()+" must be annotated with @At(n)");
Json value = data.at(at.value());
Util.setField(obj, f, deserialize(f, value));
}
// deserializes super classes
Class<?> superclazz = obj.getClass().getSuperclass();
while(superclazz!=null){
fields = superclazz.getDeclaredFields();
for (Field f : fields) {
if(mustIgnore(f)) continue;
At at = f.getAnnotation(At.class);
if(at == null) throw new SienaException("Field "+obj.getClass()+"."+f.getName()+" must be annotated with @At(n)");
Json value = data.at(at.value());
Util.setField(obj, f, deserialize(f, value));
}
superclazz = superclazz.getSuperclass();
}
return obj;
}
if(Json.class.isAssignableFrom(clazz)){
return data;
}
JsonDeserializeAs as = clazz.getAnnotation(JsonDeserializeAs.class);
if(as != null) {
if(as.value() == String.class) {
return data.asString();
}
else {
Class<?> asClazz = as.value();
Object ret = deserialize(as.value(), data);
if(JsonRestorable.class.isAssignableFrom(asClazz)){
return ((JsonRestorable<?>)ret).restore();
}
return ret;
}
}
return deserializePlain(clazz, data);
} catch(Exception e) {
throw new SienaException(e);
}
}
private static boolean mustIgnore(Field field) {
if(Query.class.isAssignableFrom(field.getType())) return true;
if(field.isAnnotationPresent(EmbedIgnore.class)) return true;
boolean b = (field.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT ||
(field.getModifiers() & Modifier.STATIC) == Modifier.STATIC ||
field.isSynthetic();
if(!field.isAccessible())
field.setAccessible(true);
return b;
}
public static Object deserialize(Field f, Json data) {
if(data == null || data.isNull()) return deserializePlain(f.getType(), data);
Class<?> clazz = f.getType();
if(Map.class.isAssignableFrom(clazz)) {
if(!data.isMap()) {
throw new SienaException("Error while deserializating field "+f.getDeclaringClass()
+"."+f.getName()+" of type "+clazz
+". A Json map is needed but found: "+data);
}
Map<String, Object> map = new HashMap<String, Object>();
for (String key : data.keys()) {
map.put(key, deserialize(Util.getGenericClass(f, 1), data.get(key)));
}
return map;
}
else if(Collection.class.isAssignableFrom(clazz)) {
if(!data.isList()) {
throw new SienaException("Error while deserializating field "+f.getDeclaringClass()
+"."+f.getName()+" of type "+clazz
+". A Json list is needed but found: "+data);
}
Collection<Object> collection = null;
if(clazz == List.class) {
collection = new ArrayList<Object>(data.size());
} else {
collection = new HashSet<Object>();
}
for (Json value : data) {
collection.add(deserialize(Util.getGenericClass(f, 0), value));
}
return collection;
}
else if(Json.class.isAssignableFrom(clazz)){
return data;
}
else if(Field.class.isAssignableFrom(clazz)){
String fieldName = data.get("fieldName").asString();
String parentClass = data.get("parentClass").asString();
try {
Class<?> cl = Class.forName(parentClass);
Field fi = cl.getField(fieldName);
return fi;
}catch(ClassNotFoundException ex){
throw new SienaException(ex);
}catch(NoSuchFieldException ex){
throw new SienaException(ex);
}
}
else if(clazz.isArray()){
Class<?> arrClazz = clazz.getComponentType();
Object arr = Array.newInstance(arrClazz, data.size());
int i=0;
for (Json value : data) {
Array.set(arr, i++, deserialize(arrClazz, value));
}
return arr;
}else if(clazz == Class.class){
String className = data.get("className").asString();
try {
Class<?> cl = Class.forName(className);
return cl;
}catch(ClassNotFoundException ex){
throw new SienaException(ex);
}
}
Format format = f.getAnnotation(Format.class);
if(format != null) {
if(f.getType() == Date.class) {
SimpleDateFormat sdf = new SimpleDateFormat(format.value());
try {
return sdf.parse(data.str());
} catch (ParseException e) {
throw new SienaException(e);
}
}
}
JsonDeserializeAs as = f.getAnnotation(JsonDeserializeAs.class);
if(as != null) {
if(as.value() == String.class) {
return data.asString();
}
else {
return deserialize(as.value(), data);
}
}
return deserialize(clazz, data);
}
private static Object deserializePlain(Class<?> type, Json data) {
if(Boolean.class == type || boolean.class == type) {
return data!=null ? data.asBoolean() : 0;
}
if(type == Byte.class || type == Byte.TYPE) {
return data!=null ? data.asBoolean() : 0;
}
else if(type == Short.class || type == Short.TYPE) {
return data!=null ? data.asShort() : 0;
}
else if(type == Integer.class || type == Integer.TYPE) {
return data!=null ? data.asInt() : 0;
}
else if(type == Long.class || type == Long.TYPE) {
return data!=null ? data.asLong() : 0;
}
else if(type == Float.class || type == Float.TYPE) {
return data!=null ? data.asFloat() : 0;
}
else if(type == Double.class || type == Double.TYPE) {
return data!=null ? data.asDouble() : 0;
}
else if(type == String.class) {
return data!=null ? data.str() : null;
}
else if(type.isEnum()) {
if(data!=null){
String str = data.str();
if(str != null) return Enum.valueOf((Class<Enum>) type, data.str());
}
return null;
}else if(type == Date.class){
return data!=null ? data.asDate() : null;
}
return null;
}
}