Skip to content

Instantly share code, notes, and snippets.

@dimzon
Created October 3, 2015 21:21
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 dimzon/de35924ad5a38ef4ac78 to your computer and use it in GitHub Desktop.
Save dimzon/de35924ad5a38ef4ac78 to your computer and use it in GitHub Desktop.
package com.github.dimzon.gson.gbson;
import com.google.gson.*;
import com.google.gson.internal.JsonReaderInternalAccess;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.bson.BSONObject;
import org.bson.types.Binary;
import org.bson.types.ObjectId;
import sun.plugin.dom.exception.InvalidStateException;
import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* User: me
* Date: 03.10.15
* Time: 1:33
*/
@SuppressWarnings("unused")
public final class Gbson {
static {
final JsonReaderInternalAccess previous = JsonReaderInternalAccess.INSTANCE;
JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() {
@Override
public void promoteNameToValue(JsonReader jsonReader) throws IOException {
if (jsonReader instanceof MongoReader) {
((MongoReader) jsonReader).promoteNameToValue();
return;
}
previous.promoteNameToValue(jsonReader);
}
};
}
private final Gson gson;
private Gbson(Gson gson) {
this.gson = gson;
}
public static Gbson newInstance() {
return newBuilder().newGbison();
}
public static Builder newBuilder() {
return new Builder();
}
public <T> DBObject toBson(T src, Class<T> tClass) {
return toBson(src, (Type) tClass);
}
public DBObject toBson(Object src) {
if (src == null) return null;
return toBson(src, src.getClass());
}
public DBObject toBson(Object src, Type typeOfSrc) throws JsonIOException {
if (src == null) return null;
MongoWriter w = new MongoWriter();
gson.toJson(src, typeOfSrc, w);
return (DBObject) w.toObj();
}
@SuppressWarnings("unchecked")
public <T> T fromBson(DBObject bson, Class<T> classOfT) throws JsonSyntaxException {
return (T) fromBson(bson, (Type) classOfT);
}
public Object fromBson(DBObject bson, Type typeOfT) throws JsonSyntaxException {
MongoReader r = new MongoReader(bson);
return gson.fromJson(r, typeOfT);
}
@SuppressWarnings("unused")
public static class Builder {
private final GsonBuilder gsonBuilder;
public Builder(GsonBuilder gsonBuilder) {
this.gsonBuilder = gsonBuilder;
Adapters.register(this);
}
public Builder() {
this(new GsonBuilder());
}
public Gbson newGbison() {
return new Gbson(gsonBuilder.create());
}
public Builder setVersion(double ignoreVersionsAfter) {
gsonBuilder.setVersion(ignoreVersionsAfter);
return this;
}
public Builder excludeFieldsWithModifiers(int... modifiers) {
gsonBuilder.excludeFieldsWithModifiers(modifiers);
return this;
}
public Builder excludeFieldsWithoutExposeAnnotation() {
gsonBuilder.excludeFieldsWithoutExposeAnnotation();
return this;
}
public Builder enableComplexMapKeySerialization() {
gsonBuilder.enableComplexMapKeySerialization();
return this;
}
public Builder disableInnerClassSerialization() {
gsonBuilder.disableInnerClassSerialization();
return this;
}
public Builder setFieldNamingPolicy(FieldNamingPolicy namingConvention) {
gsonBuilder.setFieldNamingPolicy(namingConvention);
return this;
}
public Builder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
gsonBuilder.setFieldNamingStrategy(fieldNamingStrategy);
return this;
}
public Builder setExclusionStrategies(ExclusionStrategy... strategies) {
gsonBuilder.setExclusionStrategies(strategies);
return this;
}
public Builder addSerializationExclusionStrategy(ExclusionStrategy strategy) {
gsonBuilder.addSerializationExclusionStrategy(strategy);
return this;
}
public Builder addDeserializationExclusionStrategy(ExclusionStrategy strategy) {
gsonBuilder.addDeserializationExclusionStrategy(strategy);
return this;
}
public Builder registerTypeAdapter(Type type, Object typeAdapter) {
gsonBuilder.registerTypeAdapter(type, typeAdapter);
return this;
}
public Builder registerTypeAdapterFactory(TypeAdapterFactory factory) {
gsonBuilder.registerTypeAdapterFactory(factory);
return this;
}
public Builder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) {
gsonBuilder.registerTypeHierarchyAdapter(baseType, typeAdapter);
return this;
}
}
private static class Adapters {
final static TypeAdapter<Long> LONG_TYPE_ADAPTER = new TypeAdapter<Long>() {
@Override
public void write(JsonWriter jsonWriter, Long aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public Long read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof Long) return ((Long) obj);
if (obj instanceof Number) return ((Number) obj).longValue();
try {
return Long.parseLong(obj.toString());
} catch (NumberFormatException ex) {
throw new JsonParseException(ex);
}
}
};
final static TypeAdapterFactory LONG_FACTORY = TypeAdapters.newFactory(long.class, Long.class, LONG_TYPE_ADAPTER);
final static TypeAdapter<Integer> INT_TYPE_ADAPTER = new TypeAdapter<Integer>() {
@Override
public void write(JsonWriter jsonWriter, Integer aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public Integer read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof Integer) return ((Integer) obj);
if (obj instanceof Number) return ((Number) obj).intValue();
try {
return Integer.parseInt(obj.toString());
} catch (NumberFormatException ex) {
throw new JsonParseException(ex);
}
}
};
final static TypeAdapterFactory INT_FACTORY = TypeAdapters.newFactory(int.class, Integer.class, INT_TYPE_ADAPTER);
final static TypeAdapter<Short> SHORT_TYPE_ADAPTER = new TypeAdapter<Short>() {
@Override
public void write(JsonWriter jsonWriter, Short aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public Short read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof Short) return ((Short) obj);
if (obj instanceof Number) return ((Number) obj).shortValue();
try {
return Short.parseShort(obj.toString());
} catch (NumberFormatException ex) {
throw new JsonParseException(ex);
}
}
};
final static TypeAdapterFactory SHORT_FACTORY = TypeAdapters.newFactory(short.class, Short.class, SHORT_TYPE_ADAPTER);
final static TypeAdapter<Byte> BYTE_TYPE_ADAPTER = new TypeAdapter<Byte>() {
@Override
public void write(JsonWriter jsonWriter, Byte aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public Byte read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof Byte) return ((Byte) obj);
if (obj instanceof Number) return ((Number) obj).byteValue();
try {
return Byte.parseByte(obj.toString());
} catch (NumberFormatException ex) {
throw new JsonParseException(ex);
}
}
};
final static TypeAdapterFactory BYTE_FACTORY = TypeAdapters.newFactory(byte.class, Byte.class, BYTE_TYPE_ADAPTER);
final static TypeAdapter<Float> FLOAT_TYPE_ADAPTER = new TypeAdapter<Float>() {
@Override
public void write(JsonWriter jsonWriter, Float aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public Float read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof Float) return ((Float) obj);
if (obj instanceof Number) return ((Number) obj).floatValue();
try {
return Float.parseFloat(obj.toString());
} catch (NumberFormatException ex) {
throw new JsonParseException(ex);
}
}
};
final static TypeAdapterFactory FLOAT_FACTORY = TypeAdapters.newFactory(float.class, Float.class, FLOAT_TYPE_ADAPTER);
final static TypeAdapter<Double> DOUBLE_TYPE_ADAPTER = new TypeAdapter<Double>() {
@Override
public void write(JsonWriter jsonWriter, Double aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public Double read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof Double) return ((Double) obj);
if (obj instanceof Number) return ((Number) obj).doubleValue();
try {
return Double.parseDouble(obj.toString());
} catch (NumberFormatException ex) {
throw new JsonParseException(ex);
}
}
};
final static TypeAdapterFactory DOUBLE_FACTORY = TypeAdapters.newFactory(double.class, Double.class, DOUBLE_TYPE_ADAPTER);
final static TypeAdapter<Boolean> BOOLEAN_TYPE_ADAPTER = new TypeAdapter<Boolean>() {
@Override
public void write(JsonWriter jsonWriter, Boolean aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public Boolean read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof Boolean) return ((Boolean) obj);
if (obj instanceof Number) return ((Number) obj).longValue() != 0;
return Boolean.parseBoolean(obj.toString());
}
};
final static TypeAdapterFactory BOOLEAN_FACTORY = TypeAdapters.newFactory(boolean.class, Boolean.class, BOOLEAN_TYPE_ADAPTER);
final static TypeAdapter<UUID> UUID_TYPE_ADAPTER = new TypeAdapter<UUID>() {
@Override
public void write(JsonWriter jsonWriter, UUID aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public UUID read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof UUID) return ((UUID) obj);
return UUID.fromString(obj.toString());
}
};
final static TypeAdapterFactory UUID_FACTORY = TypeAdapters.newFactory(UUID.class, UUID_TYPE_ADAPTER);
final static TypeAdapter<byte[]> BINARY_TYPE_ADAPTER = new TypeAdapter<byte[]>() {
@Override
public void write(JsonWriter jsonWriter, byte[] aLong) throws IOException {
if (aLong == null) {
jsonWriter.nullValue();
return;
}
((MongoWriter) jsonWriter).put(new Binary(aLong));
}
@Override
public byte[] read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof Binary) return ((Binary) obj).getData();
return obj.toString().getBytes(StandardCharsets.UTF_8);
}
};
final static TypeAdapterFactory BINARY_FACTORY = TypeAdapters.newFactory(byte[].class, BINARY_TYPE_ADAPTER);
final static TypeAdapter<Binary> BINARY1_TYPE_ADAPTER = new TypeAdapter<Binary>() {
@Override
public void write(JsonWriter jsonWriter, Binary aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public Binary read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof Binary) return ((Binary) obj);
return new Binary(obj.toString().getBytes(StandardCharsets.UTF_8));
}
};
final static TypeAdapterFactory BINARY1_FACTORY = TypeAdapters.newFactory(Binary.class, BINARY1_TYPE_ADAPTER);
final static TypeAdapter<ObjectId> ID_TYPE_ADAPTER = new TypeAdapter<ObjectId>() {
@Override
public void write(JsonWriter jsonWriter, ObjectId aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public ObjectId read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof ObjectId) return ((ObjectId) obj);
if (obj instanceof Binary) return new ObjectId(((Binary) obj).getData());
return new ObjectId(obj.toString());
}
};
final static TypeAdapterFactory ID_FACTORY = TypeAdapters.newFactory(ObjectId.class, ID_TYPE_ADAPTER);
final static TypeAdapter<BigDecimal> DECIMAL_TYPE_ADAPTER = new TypeAdapter<BigDecimal>() {
@Override
public void write(JsonWriter jsonWriter, BigDecimal aLong) throws IOException {
if (aLong == null) {
jsonWriter.nullValue();
return;
}
((MongoWriter) jsonWriter).put(aLong.toString());
}
@Override
public BigDecimal read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof BigDecimal) return (BigDecimal) obj;
if (obj instanceof String) return new BigDecimal((String) obj);
if (obj instanceof Long) return new BigDecimal((Long) obj);
if (obj instanceof Integer) return new BigDecimal((Integer) obj);
if (obj instanceof Double) return new BigDecimal((Double) obj);
return new BigDecimal(obj.toString());
}
};
final static TypeAdapterFactory DECIMAL_FACTORY = TypeAdapters.newFactory(BigDecimal.class, DECIMAL_TYPE_ADAPTER);
final static TypeAdapter<BigInteger> BIGINT_TYPE_ADAPTER = new TypeAdapter<BigInteger>() {
@Override
public void write(JsonWriter jsonWriter, BigInteger aLong) throws IOException {
if (aLong == null) {
jsonWriter.nullValue();
return;
}
((MongoWriter) jsonWriter).put(aLong.toString());
}
@Override
public BigInteger read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
if (obj == null) return null;
if (obj instanceof BigInteger) return (BigInteger) obj;
return new BigInteger(obj.toString());
}
};
final static TypeAdapterFactory BIGINT_FACTORY = TypeAdapters.newFactory(BigInteger.class, BIGINT_TYPE_ADAPTER);
final static TypeAdapter<java.util.Date> DATE_TYPE_ADAPTER = new TypeAdapter<java.util.Date>() {
@Override
public void write(JsonWriter jsonWriter, java.util.Date aLong) throws IOException {
((MongoWriter) jsonWriter).put(aLong);
}
@Override
public java.util.Date read(JsonReader jsonReader) throws IOException {
Object obj = ((MongoReader) jsonReader).nextObj();
return (Date) obj;
}
};
final static TypeAdapterFactory DATE_FACTORY = TypeAdapters.newFactory(Date.class, DATE_TYPE_ADAPTER);
public static void register(Builder builder) {
builder.registerTypeAdapterFactory(LONG_FACTORY);
builder.registerTypeAdapterFactory(INT_FACTORY);
builder.registerTypeAdapterFactory(SHORT_FACTORY);
builder.registerTypeAdapterFactory(BYTE_FACTORY);
builder.registerTypeAdapterFactory(FLOAT_FACTORY);
builder.registerTypeAdapterFactory(DOUBLE_FACTORY);
builder.registerTypeAdapterFactory(BOOLEAN_FACTORY);
builder.registerTypeAdapterFactory(UUID_FACTORY);
builder.registerTypeAdapterFactory(BINARY_FACTORY);
builder.registerTypeAdapterFactory(BINARY1_FACTORY);
builder.registerTypeAdapterFactory(ID_FACTORY);
builder.registerTypeAdapterFactory(DECIMAL_FACTORY);
builder.registerTypeAdapterFactory(BIGINT_FACTORY);
builder.registerTypeAdapterFactory(DATE_FACTORY);
//builder.registerTypeAdapter()
}
}
private static class MongoReader extends JsonReader {
private static final java.io.Reader NullReader = new java.io.Reader() {
@SuppressWarnings("NullableProblems")
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
}
};
private static final Object SENTINEL_CLOSED = new Object();
private final List<Object> stack = new ArrayList<Object>();
public MongoReader(DBObject basicDBObject) {
super(NullReader);
stack.add(basicDBObject);
}
public Object nextObj() throws IOException {
JsonToken peek = peek();
if (peek == JsonToken.NULL || peek == JsonToken.BOOLEAN || peek == JsonToken.NUMBER || peek == JsonToken.STRING)
return popStack();
throw new InvalidStateException("peek=" + peek);
}
@Override
public void beginArray() throws IOException {
expect(JsonToken.BEGIN_ARRAY);
JsonArray array = (JsonArray) peekStack();
stack.add(array.iterator());
}
@Override
public void endArray() throws IOException {
expect(JsonToken.END_ARRAY);
popStack(); // empty iterator
popStack(); // array
}
@Override
public void beginObject() throws IOException {
expect(JsonToken.BEGIN_OBJECT);
BSONObject object = (BSONObject) peekStack();
if (object instanceof Map) {
stack.add(((Map) object).entrySet().iterator());
} else {
stack.add(object.toMap().entrySet().iterator());
}
}
@Override
public void endObject() throws IOException {
expect(JsonToken.END_OBJECT);
popStack(); // empty iterator
popStack(); // object
}
@Override
public boolean hasNext() throws IOException {
JsonToken token = peek();
return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY;
}
@Override
public JsonToken peek() throws IOException {
if (stack.isEmpty()) {
return JsonToken.END_DOCUMENT;
}
Object o = peekStack();
if (o instanceof Iterator) {
boolean isObject = !(stack.get(stack.size() - 2) instanceof List);
Iterator<?> iterator = (Iterator<?>) o;
if (iterator.hasNext()) {
if (isObject) {
return JsonToken.NAME;
} else {
stack.add(iterator.next());
return peek();
}
} else {
return isObject ? JsonToken.END_OBJECT : JsonToken.END_ARRAY;
}
} else if (o instanceof List) {
return JsonToken.BEGIN_ARRAY;
} else if (o instanceof BSONObject) {
return JsonToken.BEGIN_OBJECT;
} else if (o instanceof Boolean) {
return JsonToken.BOOLEAN;
} else if (o instanceof Number) {
return JsonToken.NUMBER;
} else if (o == null) {
return JsonToken.NULL;
} else if (o == SENTINEL_CLOSED) {
throw new IllegalStateException("JsonReader is closed");
} else {
return JsonToken.STRING;
}
}
private Object peekStack() {
return stack.get(stack.size() - 1);
}
private Object popStack() {
return stack.remove(stack.size() - 1);
}
private void expect(JsonToken expected) throws IOException {
if (peek() != expected) {
throw new IllegalStateException("Expected " + expected + " but was " + peek());
}
}
@Override
public String nextName() throws IOException {
expect(JsonToken.NAME);
Iterator<?> i = (Iterator<?>) peekStack();
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
stack.add(entry.getValue());
return (String) entry.getKey();
}
@Override
public String nextString() throws IOException {
return nextObj().toString();
}
@Override
public boolean nextBoolean() throws IOException {
expect(JsonToken.BOOLEAN);
return ((Boolean) popStack());
}
@Override
public void nextNull() throws IOException {
expect(JsonToken.NULL);
popStack();
}
@Override
public double nextDouble() throws IOException {
expect(JsonToken.NUMBER);
return ((Number) popStack()).doubleValue();
}
@Override
public long nextLong() throws IOException {
expect(JsonToken.NUMBER);
return ((Number) popStack()).longValue();
}
@Override
public int nextInt() throws IOException {
expect(JsonToken.NUMBER);
return ((Number) popStack()).intValue();
}
@Override
public void close() throws IOException {
stack.clear();
stack.add(SENTINEL_CLOSED);
}
@Override
public void skipValue() throws IOException {
if (peek() == JsonToken.NAME) {
nextName();
} else {
popStack();
}
}
@Override
public String toString() {
return getClass().getSimpleName();
}
public void promoteNameToValue() throws IOException {
expect(JsonToken.NAME);
Iterator<?> i = (Iterator<?>) peekStack();
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
stack.add(entry.getValue());
stack.add(entry.getKey());
}
}
private static class MongoWriter extends JsonWriter {
private static final java.io.Writer NullWriter = new java.io.Writer() {
@SuppressWarnings("NullableProblems")
@Override
public void write(char[] buffer, int off, int len) throws IOException {
}
@Override
public void flush() throws IOException {
}
@Override
public void close() throws IOException {
}
};
final ListAppender listAppender;
private final Stack<Appender> appenderStack = new Stack<Appender>();
public MongoWriter() {
super(NullWriter);
appenderStack.push(listAppender = new ListAppender());
}
public Object toObj() {
return listAppender.basicDBList.get(0);
}
@Override
public JsonWriter beginArray() throws IOException {
ListAppender appender = new ListAppender();
appenderStack.peek().value(appender.basicDBList);
appenderStack.push(appender);
return this;
}
@Override
public JsonWriter endArray() throws IOException {
appenderStack.pop();
return this;
}
@Override
public JsonWriter beginObject() throws IOException {
DocAppender appender = new DocAppender();
appenderStack.peek().value(appender.basicDBObject);
appenderStack.push(appender);
return this;
}
@Override
public JsonWriter endObject() throws IOException {
appenderStack.pop();
return this;
}
@Override
public JsonWriter name(String name) throws IOException {
((DocAppender) appenderStack.peek()).name = name;
return this;
}
@Override
public JsonWriter nullValue() throws IOException {
put(null);
return this;
}
@Override
public JsonWriter value(String value) throws IOException {
put(value);
return this;
}
@Override
public JsonWriter value(boolean value) throws IOException {
put(value);
return this;
}
@Override
public JsonWriter value(double value) throws IOException {
put(value);
return this;
}
@Override
public JsonWriter value(long value) throws IOException {
put(value);
return this;
}
@Override
public JsonWriter value(Number value) throws IOException {
put(value);
return this;
}
@Override
public void flush() throws IOException {
}
@Override
public void close() throws IOException {
}
@Override
public boolean isLenient() {
return true;
}
public void put(Object obj) {
appenderStack.peek().value(obj);
}
private static abstract class Appender {
public abstract void value(Object value);
}
private static class DocAppender extends Appender {
BasicDBObject basicDBObject = new BasicDBObject();
String name;
@Override
public void value(Object value) {
basicDBObject.append(name, value);
}
}
private static class ListAppender extends Appender {
BasicDBList basicDBList = new BasicDBList();
@Override
public void value(Object value) {
basicDBList.add(value);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment