Вопрос-ответ

How do I write a custom JSON deserializer for Gson?

Как мне написать пользовательский десериализатор JSON для Gson?

У меня есть класс Java, User:

public class User
{
int id;
String name;
Timestamp updateDate;
}

И я получаю список JSON, содержащий пользовательские объекты, из веб-сервиса:

[{"id":1,"name":"Jonas","update_date":"1300962900226"},
{"id":5,"name":"Test","date_date":"1304782298024"}]

Я пытался написать пользовательский десериализатор:

@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));
}

Но мой десериализатор не работает. Как я могу написать пользовательский десериализатор JSON для Gson?

Переведено автоматически
Ответ 1

Я бы выбрал несколько иной подход следующим образом, чтобы свести к минимуму "ручной" синтаксический анализ в моем коде, поскольку излишнее выполнение других действий несколько противоречит цели, по которой я вообще использовал API, подобный Gson.

// 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);
}
}

(Предполагается, что в исходном вопросе "date_date" должно быть "update_date".)

Ответ 2
@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()));
}

Я предполагаю, что пользовательский класс имеет соответствующий конструктор.

Ответ 3

Сегодня я искал эту вещь, поскольку у моего класса была java.time.Instant и gson по умолчанию не смог ее десериализовать. Мои POJOs выглядят следующим образом:

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
)

Затем для Instant переменных я анализирую временные переменные json и преобразую string в Instant . Для integer , string и т.д. Я использую jsonObject.get("id").asInt etc. Для других pojo я использую десериализатор по умолчанию, подобный этому:

val rewardItem: RewardItem = context!!.deserialize(rewardJsonElement,
RewardItem::class.java);

Итак, соответствующий пользовательский десериализатор выглядит следующим образом:

  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;
}

}

Наконец, я создаю свой пользовательский gson следующим образом:

 val gsonBuilder = GsonBuilder();
gsonBuilder.registerTypeAdapter(RewardResult::class.javaObjectType,
this.customJsonDeserializer);
val customGson: Gson = gsonBuilder.create();
java json