summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/util/Json.java
blob: d9fb65946d6547008ad8921d380dbf4d6ae0daac (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package org.openslx.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.log4j.Logger;
import org.apache.thrift.TBase;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonSyntaxException;

public class Json {

	private static final Logger LOGGER = Logger.getLogger(Json.class);

	/**
	 * Global static instance. The Gson object is thread-safe.
	 */
	private static final AtomicReference<Gson> gsonRef = new AtomicReference<>();

	private static final GsonBuilder gsonThriftBuilder = new GsonBuilder();
	
	public static <T extends TBase<?, ?>> void registerThriftClass(Class<T> thriftClass) {
		// Determine all relevant fields
		Field[] fieldArray = thriftClass.getFields();
		List<ThriftField> fields = new ArrayList<>(fieldArray.length);
		for ( Field field : fieldArray ) {
			if ( "__isset_bitfield".equals( field.getName() ) )
				continue;
			if ( Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())
					|| Modifier.isTransient( field.getModifiers() ))
				continue;
			String upperName = field.getName().substring(0, 1).toUpperCase()
					+ field.getName().substring(1);
			try {
				fields.add( new ThriftField( field,
						thriftClass.getMethod( "get" + upperName ),
						thriftClass.getMethod( "set" + upperName, field.getType() ),
						thriftClass.getMethod( "isSet" + upperName) ) );
			} catch (NoSuchMethodException e) {
				// Not a thrift field, apparently
			}
		}
		synchronized ( Json.class ) {
			gsonThriftBuilder.registerTypeAdapter(thriftClass, new JsonThriftHandler<T>(thriftClass, fields));
			gsonRef.set( null );
		}
	}
	
	private static Gson getInstance()
	{
		Gson gson = gsonRef.get();
		if (gson == null) {
			synchronized ( Json.class ) {
   			gson = gsonThriftBuilder.create();
   			gsonRef.set( gson );
			}
		}
		return gson;
	}

	/**
	 * Deserialize the given json string to an instance of T.
	 * This will deserialize all fields, except transient ones.
	 * 
	 * @param data JSON formatted data
	 * @param classOfData class to instantiate
	 * @return instanceof T
	 */
	public static <T> T deserialize(String data, Class<T> classOfData) {
		try {
			return getInstance().fromJson(data, classOfData);
		} catch (JsonSyntaxException e) {
			LOGGER.warn("Cannot deserialize to " + classOfData.getSimpleName(), e);
			return null;
		}
	}

	/**
	 * Serialize the given POJO. All fields except transient ones will be
	 * serialized.
	 * 
	 * @param object some object to serialize
	 * @return JSON formatted represenatation of <code>object</code>
	 */
	public static String serialize(Object object) {
		return getInstance().toJson(object);
	}

	private static class JsonThriftHandler<T> implements JsonDeserializer<T>, JsonSerializer<T> {
		private final Class<T> clazz;
		private final List<ThriftField> fields;

		public JsonThriftHandler(Class<T> classOfData, List<ThriftField> fields) {
			this.clazz = classOfData;
			this.fields = fields;
		}

		@Override
		public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
				throws JsonParseException {
			if (!(json instanceof JsonObject))
				throw new JsonParseException("Need a json object, have " + json.getClass().getSimpleName());
			JsonObject obj = (JsonObject) json;
			final T inst;
			try {
				inst = clazz.newInstance();
			} catch (Exception e) {
				LOGGER.warn("Could not deserialize to class " + clazz.getName(), e);
				throw new JsonParseException("Cannot instantiate class " + clazz.getSimpleName());
			}
			for (ThriftField field : fields) {
				JsonElement element = obj.get(field.field.getName());
				if (element == null || element.isJsonNull())
					continue;
				try {
					field.setter.invoke(inst, context.deserialize(element, field.field.getType()));
				} catch (Exception e) {
					LOGGER.warn("Could not call " + field.setter.getName() + " on " + clazz.getSimpleName(), e);
				}
			}
			return inst;
		}

		@Override
		public JsonElement serialize( T thriftClass, Type typeOfT, JsonSerializationContext context )
		{
			JsonObject o = new JsonObject();
			for ( ThriftField thrift : fields ) {
				try {
					if ( !(Boolean)thrift.isset.invoke( thriftClass ) )
						continue;
					Object value = thrift.getter.invoke( thriftClass );
					if ( value == null )
						continue;
					JsonElement jo = null;
					if ( value instanceof Number ) {
						jo = new JsonPrimitive( (Number)value );
					} else if ( value instanceof String ) {
						jo = new JsonPrimitive( (String)value );
					} else if ( value instanceof Character ) {
						jo = new JsonPrimitive( (Character)value );
					} else if ( value instanceof Boolean ) {
						jo = new JsonPrimitive( (Boolean)value );
					} else {
						jo = context.serialize( value );
					}
					o.add( thrift.field.getName(), jo );
				} catch ( Exception e ) {
					LOGGER.warn( "Cannot serialize field " + thrift.field.getName() + " of thift class "
							+ thriftClass.getClass().getSimpleName(), e );
				}
			}
			return o;
		}

	}
	
	private static class ThriftField
	{
		public final Method getter, setter, isset;
		public final Field field;
		ThriftField(Field field, Method getter, Method setter, Method isset)
		{
			this.field = field;
			this.getter = getter;
			this.setter = setter;
			this.isset = isset;
		}
	}

}