I am using the Retrofit library for my REST calls. Most of what I have done has been smooth as butter but for some reason I am having issues converting JSON timestamp strings into java.util.Date objects. The JSON that is coming in looks like this.
{
"date": "2013-07-16",
"created_at": "2013-07-16T22:52:36Z",
}
How can I tell Retrofit or Gson to convert these strings into java.util.Date objects?
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(API_BASE_URL)
.setConverter(new GsonConverter.create(gson))
.build();
Or the Kotlin equivalent:
val gson = GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create()
RestAdapter restAdapter = Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(T::class.java)
You can set your customized Gson parser to retrofit. More here: Retrofit Website
Look at Ondreju's response to see how to implement this in retrofit 2.
#gderaco's answer updated to retrofit 2.0:
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();
Retrofit retrofitAdapter = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Here is how I did it:
Create DateTime class extending Date and then write a custom deserializer:
public class DateTime extends java.util.Date {
public DateTime(long readLong) {
super(readLong);
}
public DateTime(Date date) {
super(date.getTime());
}
}
Now for the deserializer part where we register both Date and DateTime converters:
public static Gson gsonWithDate(){
final GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
#Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
return df.parse(json.getAsString());
} catch (final java.text.ParseException e) {
e.printStackTrace();
return null;
}
}
});
builder.registerTypeAdapter(DateTime.class, new JsonDeserializer<DateTime>() {
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
#Override
public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
return new DateTime(df.parse(json.getAsString()));
} catch (final java.text.ParseException e) {
e.printStackTrace();
return null;
}
}
});
return builder.create();
}
And when you create your RestAdapter, do the following:
new RestAdapter.Builder().setConverter(gsonWithDate());
Your Foo should look like this:
class Foo {
Date date;
DateTime created_at;
}
Gson can handle only one datetime format (those specified in builder) plus the iso8601 if parsing with custom format is not possible. So, a solution could be to write your custom deserializer. To solve your problem I defined:
package stackoverflow.questions.q18473011;
import java.util.Date;
public class Foo {
Date date;
Date created_at;
public Foo(Date date, Date created_at){
this.date = date;
this.created_at = created_at;
}
#Override
public String toString() {
return "Foo [date=" + date + ", created_at=" + created_at + "]";
}
}
with this deserializer:
package stackoverflow.questions.q18473011;
import java.lang.reflect.Type;
import java.text.*;
import java.util.Date;
import com.google.gson.*;
public class FooDeserializer implements JsonDeserializer<Foo> {
public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String a = json.getAsJsonObject().get("date").getAsString();
String b = json.getAsJsonObject().get("created_at").getAsString();
SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sdfDateWithTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Date date, created;
try {
date = sdfDate.parse(a);
created = sdfDateWithTime.parse(b);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return new Foo(date, created);
}
}
Final step is to create a Gson instance with right adapter:
package stackoverflow.questions.q18473011;
import com.google.gson.*;
public class Question {
/**
* #param args
*/
public static void main(String[] args) {
String s = "{ \"date\": \"2013-07-16\", \"created_at\": \"2013-07-16T22:52:36Z\"}";
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Foo.class, new FooDeserializer());
Gson gson = builder.create();
Foo myObject = gson.fromJson(s, Foo.class);
System.out.println("Result: "+myObject);
}
}
My result:
Result: Foo [date=Tue Jul 16 00:00:00 CEST 2013, created_at=Tue Jul 16 22:52:36 CEST 2013]
Quite literally if you already have an Date object with the name "created_at" in the class you are creating then it is this easy:
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").create();
YourObject parsedObject1 = gson.fromJson(JsonStringYouGotSomehow, YourObject.class);
And you're done. no complicated overriding needed.
You can define two new classes like this:
import java.util.Date;
public class MyDate extends Date {
}
and
import java.util.Date;
public class CreatedAtDate extends Date {
}
Your POJO will be like this:
import MyDate;
import CreatedAtDate;
public class Foo {
MyDate date;
CreatedAtDate created_at;
}
Finally set your custom deserializer:
public class MyDateDeserializer implements JsonDeserializer<Date> {
public static final SimpleDateFormat sServerDateDateFormat = new SimpleDateFormat("yyyy-MM-dd");
#Override
public MyDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json != null) {
final String jsonString = json.getAsString();
try {
return (MyDate) sServerDateDateFormat.parse(jsonString);
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
}
and
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MyDate.class, new MyDateDeserializer());
This doesn't answer directly the question asked, but is in my opinion the "state of the art" if the coder has the full freedom of choice on how to solve the problem.
First of all, it's not best solution to use java.util.Date. Reason is that those classes had no ideal behaviour in some corner cases so where superseeded by the Java Instant class etc. check the answer of Basil Bourque in this S.O. question: Creating Date objects in Kotlin for API level less than or equal to 16
So I used the Instant class of ThreeTenABP, and
using Kotlin, on Android:
val gson = GsonBuilder().registerTypeAdapter(Instant::class.java,
JsonDeserializer<Instant> { json: JsonElement, _: Type?, _: JsonDeserializationContext? ->
ZonedDateTime.parse(
json.asJsonPrimitive.asString
).toInstant()
}
).create()
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
Related
Currently I am using Gson to convert an object to json
Custom Adapter
public class MyDataMsgSerializer implements JsonSerializer<MyDataMsg>, JsonDeserializer<MyDataMsg>{
private static Gson gson;
static {
try {
gson = new GsonBuilder().registerTypeHierarchyAdapter(BsCal.class, new CalendarSerializer()).setDateFormat("dd-MM-yyyy HH:mm:ss").serializeNulls().create();
} catch (Exception e) {
}
}
#Override
public JsonElement serialize(MyDataMsgsrc, Type typeOfSrc, JsonSerializationContext context) {
JsonElement jsonSubscription = gson.toJsonTree(src, typeOfSrc);
return jsonSubscription;
}
#Override
public MyDataMsg deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return gson.fromJson(json, MyDataMsg.class);
}
}
private String convertObjToJson(MyDataMsg myDataMsg) {
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(BsCal.class, new CalendarSerializer()).setDateFormat("dd-MM-yyyy HH:mm:ss")
.registerTypeHierarchyAdapter(MyDataMsg.class, new MyDataMsgSerializer()).serializeNulls()
.create();
String jsonToString = gson.toJson(myDataMsg);
LOGGER.info("Converted Json Object {}", jsonToString);
return jsonToString;
}
output:
"myDataMsg": {
"emplId": "15163",
"mnthCd": "202211",
"empltype": "M",
"workingCd": "wfh",
"btchnm": "0499",
"shift": "1",
"shiftStTm": {
"dateTime": "2022-11-30 00:00:00”,
"timeInMs": 1669766400000
},
"actncd": "UPDATE",
"IsLead": null,
"nightSf": "2",
"overtm": null,
"totalOverTm": null,
"ExtraSal": "630"
}
Currently the 'shiftStTm' is being generated as below
"shiftStTm": {
"dateTime": "2022-11-30 00:00:00",
"timeInMs": 1669766400000
},
I want it to be generated as this (key value)
"shiftStTm":"2022-11-30 00:00:00"
could any one help me here to format it to key value using GsonBuilder??
I am using GsonBuilder to convert MyDataMsg to json In which the date is formatting as object instead of key vale, could you please help me to fix the issue?
I created a custom Date deserializer:
private static class DateJsonDeserializer implements JsonDeserializer<Date>{
#Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
for (String format : DATE_FORMATS) {
try {
SimpleDateFormat df = new SimpleDateFormat(format);
return df.parse(json.getAsString());
} catch (ParseException e) {
}
}
throw new JsonParseException("Unparseable date: \"" + json.getAsString()
+ "\". Supported formats: " + Arrays.toString(DATE_FORMATS));
}
}
I added this to my gsonBuilder:
gsonBuilder.registerTypeAdapter(Date.class, new DateJsonDeserializer());
But if I would like to parse a date I see the built in DateTypeAdapter class used, why? I would like to use my date deserializer.
I use retrofit 2.1.0, Gson 2.8.0, converter-gson 2.1.0
are you sure that Date.class is the correct class? Maybe it uses a subclass of Date.class ?
the problem is in the Postegres timestamp serialization using GSON,
private static final GsonBuilder GSON_BASE = Converters
.registerAll(new GsonBuilder().disableHtmlEscaping())
.registerTypeAdapter(InfoTransfer.class, new InfoTransfer.Adapter())
.setDateFormat(DateFormat.FULL, DateFormat.FULL) //Line added but it seems work for MySQL DB Timestamp
.setPrettyPrinting()
.create();
.
//inner class in InfoTransfer
public static class Adapter implements JsonSerializer<InfoTransfer>{
#Override
public JsonElement serialize(InfoTransfer src, Type typeOfSrc, JsonSerializationContext context) {
Gson gson= new Gson();
JsonObject jsonObject= (JsonObject) gson.toJsonTree(src);
return new JsonPrimitive(jsonObject.getAsString());
}
}
.
.
.
Log.d("Result",GSON_BASE.toJson(data));
expected result :
"created_at": "2016-10-13 18:18:51.64208+01"
result :
\"created_at\": \"\\u0000\\u0001\\ufffdM\\u0015q\\ufffd}\"
so any suggestion ?
The date format can be set as follows:-
setDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSX")
Example:-
public static void main(String[] args) {
String jsonString = "{\"customorId\":\"506\",\"joiningDate\":\"2016-10-26 19:49:17.290671+01\"}";
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSX")
.setPrettyPrinting()
.create();
//Deserialize
Customer customer = gson.fromJson(jsonString, Customer.class);
System.out.println(customer.toString());
//Serialize
Customer customerSerialize = new Customer();
customerSerialize.setCustomorId("123");
customerSerialize.setJoiningDate(new Date());
System.out.println(gson.toJson(customerSerialize));
}
Customer class:-
public class Customer implements Serializable {
private static final long serialVersionUID = 1100012615187080642L;
private String customorId;
private Date joiningDate;
}
Sample output:-
Customer [customorId=506, joiningDate=Wed Oct 26 19:54:07 BST 2016]
{
"customorId": "123",
"joiningDate": "2016-10-26 20:23:37.000811+01"
}
I am trying to persist a java object having java.util.Date field in mongo collection using fasterxml jackson.
The problem is the default nature of objectMapper is to store Date as NumberLong type.
For e.g , a createdTime field of java.util.Date type gets stored as below:
"createdTime" : NumberLong("1427728445176")
I want to store it in ISODate format which is available in mongo Shell.
Now, i know there is way to format object mapper to store Date in a String dateformat.
But I am ONLY looking for ISODate() format.
For e.g
"createdTime" : ISODate("2015-01-20T16:39:42.132Z")
Is there a way to do that ?
Please advise gurus .
Thanks in advance for help.
What you need is the Jackson Joda Module. If you import that into your classpath, you can do the following on your mapper to write it as your desired Timestamp:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
mapper.writeValueAsString(date);
You can replace date in the code sample above with your POJO as necessary.
Edit:
It looks like what you really want is a custom serializer. That would look something like this:
public class IsoDateSerializer extends JsonSerializer<DateTime> {
#Override
public void serialize(DateTime value, JsonGenerator jgen, SerializerProvider provider) {
String isoDate = ISODateTimeFormat.dateTime().print(value);
jgen.writeRaw("ISODATE(\"" + isoDate + "\")");
}
Then you'll either register it on the mapper for all DateTime types
mapper.addSerializer(DateTime.class, new IsoDateSerializer());
or specify it on the function using annotations
#JsonSerializer(using = IsoDateSerializer.class)
public DateTime createdTime;
I was able to serialize the date string to ISODate format. I wrote a customer date serializer like below.
public void serialize(Date date, JsonGenerator jgen, SerializerProvider provider) throws IOException {
String dateValue = getISODateString(date);
String text = "{ \"$date\" : \""+ dateValue +"\"}";
jgen.writeRawValue(text);
}
Based on request from user #mmx73, I am adding code for customer Date DeSeriaizer.
public class IsoDateDeSerializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
String dateValue = node.get("$date").asText();
//DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
Date date = null;
try {
date = df.parse(dateValue);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
None of these answers accomplished what I wanted. I was having trouble because when I serialized the JSON string to MongoDB, it was stored as a String. A nicely formatted string, but a string none the less.
I use the com.fasterxml.jackson.databind.ObjectMapper to convert my objects to/from JSON and I wanted to continue using this class. I have the following method:
public enum JsonIntent {NONE, MONGODB};
public static ObjectMapper getMapper(final JsonIntent intent) {
ObjectMapper mapper = new ObjectMapper();
// Setting to true saves the date as NumberLong("1463597707000")
// Setting to false saves the data as "2016-05-18T19:30:52.000+0000"
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.registerModule(new JodaModule());
if (intent == JsonIntent.MONGODB) {
// If you want a date stored in MONGO as a date, then you must store it in a way that MONGO
// is able to deal with it.
SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null, null, null));
testModule.addSerializer(Date.class, new StdSerializer<Date>(Date.class) {
private static final long serialVersionUID = 1L;
#Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
try {
if (value == null) {
jgen.writeNull();
} else {
jgen.writeStartObject();
jgen.writeFieldName("$date");
String isoDate = ISODateTimeFormat.dateTime().print(new DateTime(value));
jgen.writeString(isoDate);
jgen.writeEndObject();
}
} catch (Exception ex) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Couldn't format timestamp " + value + ", writing 'null'", ex);
jgen.writeNull();
}
}
});
testModule.addDeserializer(Date.class, new StdDeserializer<Date>(Date.class) {
private static final long serialVersionUID = 1L;
#Override
public Date deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
JsonNode tree = jp.readValueAsTree();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
try {
return ISODateTimeFormat.dateTime().parseDateTime(tree.get("$date").textValue()).toDate();
} catch (Throwable t) {
throw new IOException(t.getMessage(), t);
}
}
});
mapper.registerModule(testModule);
}
return mapper;
}
Now, I can run the following test code:
BObjectMapper mapper = getMapper(JsonUtil.JsonIntent.NONE);
Date d1 = new Date();
String v = mapper.writeValueAsString(d1);
System.out.println("Joda Mapping: " + v);
Date d2 = mapper.readValue(v, Date.class);
System.out.println("Decoded Joda: " + d2);
mapper = getMapper(JsonUtil.JsonIntent.MONGODB);
v = mapper.writeValueAsString(d1);
System.out.println("Mongo Mapping: " + v);
d2 = mapper.readValue(v, Date.class);
System.out.println("Decoded Mongo: " + d2);
The output is as follows:
Joda Mapping: "2016-06-13T14:58:11.937+0000"
Decoded Joda: Mon Jun 13 10:58:11 EDT 2016
Mongo Mapping: {"$date":"2016-06-13T10:58:11.937-04:00"}
Decoded Mongo: Mon Jun 13 10:58:11 EDT 2016
Note that the JSON that will be sent to MONGODB defines the value containing a field named "$date". This tells MongoDB that this is a date object it seems.
When I look at Mongo, I see the following:
"importDate" : ISODate("2016-05-18T18:55:07Z")
Now, I can access the field as a date rather than as a string.
To add an encoded JSON string to Mongo, my code is as follows:
MongoDatabase db = getDatabase();
Document d = Document.parse(json);
db.getCollection(bucket).insertOne(d);
In this case, "json" is the encoded JSON string. Because it is coming from a JSON string, it has no way of knowing the types unless it infers this, which is why we needed the "$date" portion. The "bucket" is just a string indicating which table to use.
As a side note, I found out that if I pull a BSON object from Mongo and convert it to a JSON string by calling doc.toJson() (where doc is of type org.bison.Document as returned from a query), the date object is stored with a long value rather than a formatted text string. I did not check to see if I could push data into mongo after formatting in this way, but, you can modify the deserializer shown above to support this as follows:
testModule.addDeserializer(Date.class, new StdDeserializer<Date>(Date.class) {
private static final long serialVersionUID = 1L;
#Override
public Date deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
JsonNode tree = jp.readValueAsTree();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
try {
// Mongo will return something that looks more like:
// {$date:<long integer for milliseconds>}
// so handle that as well.
JsonNode dateNode = tree.get("$date");
if (dateNode != null) {
String textValue = dateNode.textValue();
if (!Util.IsNullOrEmpty(textValue)) {
return ISODateTimeFormat.dateTime().parseDateTime(textValue).toDate();
}
return Util.MillisToDate(dateNode.asLong());
}
return null;
} catch (Throwable t) {
Util.LogIt("Exception: " + t.getMessage());
throw new IOException(t.getMessage(), t);
}
}
});
You can convert milliseconds to a Date or DateTime as follows:
/**
* Convert milliseconds to a date time. If zero or negative, just return
* null.
*
* #param milliseconds
* #return
*/
public static Date MillisToDate(final long milliseconds) {
if (milliseconds < 1) {
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(milliseconds);
return calendar.getTime();
}
public static DateTime MillisToDateTime(final long milliseconds) {
if (milliseconds < 1) {
return null;
}
return new DateTime(milliseconds);
}
In case you get some message like
com.fasterxml.jackson.core.JsonGenerationException: Can not write a field name, expecting a value.
Make sure you use writeRawValue instead within the accepted answer. That ends the field correctly, otherwise the next field to be serialize may throw this error.
You can solve this issue by reading/writing bson instead of json. Here is a test class:
package com.nagra.jongo.mapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import de.undercouch.bson4jackson.BsonFactory;
import de.undercouch.bson4jackson.deserializers.BsonDateDeserializer;
import de.undercouch.bson4jackson.serializers.BsonDateSerializer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.Date;
/**
* Uses Bson4Jackson 2.9.0
*
* <!-- https://mvnrepository.com/artifact/de.undercouch/bson4jackson -->
* <dependency>
* <groupId>de.undercouch</groupId>
* <artifactId>bson4jackson</artifactId>
* <version>2.9.2</version>
* </dependency>
*/
public class ObjectMapperTest {
private ObjectMapper mapper = new ObjectMapper(new BsonFactory());
private static class WrappedDate {
private Date Date = new Date(0);
public WrappedDate() {
}
public Date getDate() {
return Date;
}
public void setDate(Date Date) {
this.Date = Date;
}
}
#Before
public void setUp() {
SimpleModule module = new SimpleModule();
module.addSerializer(Date.class, new BsonDateSerializer());
module.addDeserializer(Date.class, new BsonDateDeserializer());
mapper.registerModule(module);
}
#Test
public void testDate() throws IOException {
WrappedDate date = new WrappedDate();
byte[] b = mapper.writeValueAsBytes(date);
WrappedDate i = mapper.readValue(b, WrappedDate.class);
Assert.assertEquals(date.getDate(), i.getDate());
System.out.println(i.getDate());
}}
I have a Java class, User:
public class User
{
int id;
String name;
Timestamp updateDate;
}
And I receive a JSON list containing user objects from a webservice:
[{"id":1,"name":"Jonas","update_date":"1300962900226"},
{"id":5,"name":"Test","date_date":"1304782298024"}]
I have tried to write a custom deserializer:
#Override
public User deserialize(JsonElement json, Type type,
JsonDeserializationContext context) throws JsonParseException {
return new User(
json.getAsJsonPrimitive().getAsInt(),
json.getAsString(),
json.getAsInt(),
(Timestamp)context.deserialize(json.getAsJsonPrimitive(),
Timestamp.class));
}
But my deserializer doesn't work. How can I write a custom JSON deserializer for Gson?
I'd take a slightly different approach as follows, so as to minimize "manual" parsing in my code, as unnecessarily doing otherwise somewhat defeats the purpose of why I'd use an API like Gson in the first place.
// output:
// [User: id=1, name=Jonas, updateDate=2011-03-24 03:35:00.226]
// [User: id=5, name=Test, updateDate=2011-05-07 08:31:38.024]
// using java.sql.Timestamp
public class Foo
{
static String jsonInput =
"[" +
"{\"id\":1,\"name\":\"Jonas\",\"update_date\":\"1300962900226\"}," +
"{\"id\":5,\"name\":\"Test\",\"update_date\":\"1304782298024\"}" +
"]";
public static void main(String[] args)
{
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
gsonBuilder.registerTypeAdapter(Timestamp.class, new TimestampDeserializer());
Gson gson = gsonBuilder.create();
User[] users = gson.fromJson(jsonInput, User[].class);
for (User user : users)
{
System.out.println(user);
}
}
}
class User
{
int id;
String name;
Timestamp updateDate;
#Override
public String toString()
{
return String.format(
"[User: id=%1$d, name=%2$s, updateDate=%3$s]",
id, name, updateDate);
}
}
class TimestampDeserializer implements JsonDeserializer<Timestamp>
{
#Override
public Timestamp deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException
{
long time = Long.parseLong(json.getAsString());
return new Timestamp(time);
}
}
(This assumes that "date_date" should be "update_date", in the original question.)
#Override
public User deserialize(JsonElement json, Type type,
JsonDeserializationContext context) throws JsonParseException {
JsonObject jobject = json.getAsJsonObject();
return new User(
jobject.get("id").getAsInt(),
jobject.get("name").getAsString(),
new Timestamp(jobject.get("update_date").getAsLong()));
}
I'm assuming User class has the appropriate constructor.
Today I was looking for this thing as my class had java.time.Instant and the default gson could not deserialize it. My POJOs look like this:
open class RewardResult(
#SerializedName("id")
var id: Int,
#SerializedName("title")
var title: String?,
#SerializedName("details")
var details: String?,
#SerializedName("image")
var image: String?,
#SerializedName("start_time")
var startTimeUtcZulu: Instant?, // Unit: Utc / Zulu. Unit is very important
#SerializedName("end_time")
var endTimeUtcZulu: Instant?,
#SerializedName("unlock_expiry")
var unlockExpiryTimeUtcZulu: Instant?,
#SerializedName("target")
var target: Int,
#SerializedName("reward")
var rewardItem: RewardItem
);
data class RewardItem(
#SerializedName("type")
var type: String?,
#SerializedName("item_id")
var itemId: Int,
#SerializedName("amount")
var amount: Int
)
Then for Instant variables, I parse the json's time variables and convert string to Instant. For integer , string, etc I use jsonObject.get("id").asInt etc. For other pojo, I use the default deserializer like this:
val rewardItem: RewardItem = context!!.deserialize(rewardJsonElement,
RewardItem::class.java);
So the corresponding custom deserializer looks like this:
val customDeserializer: JsonDeserializer<RewardResult> = object : JsonDeserializer<RewardResult> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): RewardResult {
val jsonObject: JsonObject = json!!.asJsonObject;
val startTimeString: String? = jsonObject.get("start_time")?.asString;
var startTimeUtcZulu: Instant? = createTimeInstant(startTimeString);
val endTimeString: String? = jsonObject.get("end_time")?.asString;
var endTimeUtcZulu: Instant? = createTimeInstant(endTimeString);
val unlockExpiryStr: String? = jsonObject.get("unlock_expiry")?.asString;
var unlockExpiryUtcZulu: Instant? = createTimeInstant(unlockExpiryStr);
val rewardJsonElement: JsonElement = jsonObject.get("reward");
val rewardItem: ridmik.one.modal.reward.RewardItem = context!!.deserialize(rewardJsonElement,
ridmik.one.modal.reward.RewardItem::class.java); // I suppose this line means use the default jsonDeserializer
var output: ridmik.one.modal.reward.RewardResult = ridmik.one.modal.reward.RewardResult(
id = jsonObject.get("id").asInt,
title = jsonObject.get("title")?.asString,
details = jsonObject.get("details")?.asString,
image = jsonObject.get("image")?.asString,
startTimeUtcZulu = startTimeUtcZulu,
endTimeUtcZulu = endTimeUtcZulu,
unlockExpiryTimeUtcZulu = unlockExpiryUtcZulu,
target = jsonObject.get("target").asInt,
rewardItem = rewardItem
);
Timber.tag(TAG).e("output = "+output);
return output;
}
}
Finally, I create my custom gson like this:
val gsonBuilder = GsonBuilder();
gsonBuilder.registerTypeAdapter(RewardResult::class.javaObjectType,
this.customJsonDeserializer);
val customGson: Gson = gsonBuilder.create();