I'm looking for a way to read a MongoDB document into a POJO using GSON. It works just fine until you run into stuff like date's and longs.
I would like to write a custom adapter for Gson which will convert any BSON encoded long. Reading this post I have created my own adapter:
public class BsonLongTypeAdapter extends TypeAdapter<Long>
{
@Override
public void write(JsonWriter out, Long value) throws IOException
{
out.beginObject()
.name("$numberLong")
.value(value.toString())
.endObject();
}
@Override
public Long read(JsonReader in) throws IOException
{
in.beginObject();
assert "$numberLong".equals(in.nextName());
Long value = in.nextLong();
in.endObject();
return value;
}
}
I have defined the following tests to check if this works:
@Test
public void canWriteCorrectJSON() {
Gson gson = new GsonBuilder().registerTypeAdapter(Long.class, new BsonLongTypeAdapter()).create();
MyTestObject obj = new MyTestObject(1458569479431L);
String gsonString = gson.toJson(obj);
assertEquals("{\"timestamp\":{\"$numberLong\":\"1458569479431\"}}",gsonString);
}
@Test
public void canReadFromJSON() {
Gson gson = new GsonBuilder().registerTypeAdapter(Long.class, new BsonLongTypeAdapter()).create();
MyTestObject actualTaskObject = gson.fromJson("{\"timestamp\":{\"$numberLong\":\"1458569479431\"}}", MyTestObject.class);
MyTestObject taskObject = new MyTestObject(1458569479431L);
assertEquals(taskObject.getTimestamp(),actualTaskObject.getTimestamp());
}
private static class MyTestObject
{
long timestamp;
public MyTestObject(long ts)
{
timestamp = ts;
}
public long getTimestamp()
{
return timestamp;
}
public void setTimestamp(long timestamp)
{
this.timestamp = timestamp;
}
}
The first (write) test works just fine, but the read test fails on:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a long but was BEGIN_OBJECT at line 1 column 15 path $.timestamp
Because the read function from my adapter is never called. I presume this might be because I want to map to MyTestObject and not to Long, but I don't want to have to write adapters for all classes that contain longs.
Is it possible to write an adapter for GSON that converts all BSON longs I send into it?
I solved it using a CustomizedTypeAdapterFactory. See this question
Basically first write a customized adapter:
And then create a subclass for all classes that need to be taken into account. You do have to create one for every class containing a long (in this case). But you don't have to serialize anything but the long value (and any other bson specific values)
and then generate Gson with: