[java] Retrofit GSON은 json 문자열에서 java.util.date로 날짜를 직렬화합니다.

REST 호출에 Retrofit 라이브러리를 사용하고 있습니다. 내가 한 대부분은 버터처럼 부드럽지만 어떤 이유로 JSON 타임 스탬프 문자열을 java.util.Date객체 로 변환하는 데 문제가 있습니다. 들어오는 JSON은 다음과 같습니다.

{
    "date": "2013-07-16",
    "created_at": "2013-07-16T22:52:36Z",
}

Retrofit 또는 Gson에게 이러한 문자열을 변환하도록 어떻게 알릴 수 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();

또는 이에 상응하는 Kotlin :

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)

사용자 정의 된 Gson 파서를 개조하도록 설정할 수 있습니다. 더 많은 정보 : Retrofit 웹 사이트

이를 개조 2에서 구현하는 방법을 보려면 Ondreju의 응답을 살펴보십시오.


답변

@gderaco의 답변이 개조 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();


답변

내가 한 방법은 다음과 같습니다.

Date를 확장하는 DateTime 클래스를 만든 다음 사용자 지정 deserializer를 작성합니다.

public class DateTime extends java.util.Date {

    public DateTime(long readLong) {
        super(readLong);
    }

    public DateTime(Date date) {
        super(date.getTime());
    }
}

이제 Date 및 DateTime 변환기를 모두 등록하는 deserializer 부분에 대해 :

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

RestAdapter를 만들 때 다음을 수행하십시오.

new RestAdapter.Builder().setConverter(gsonWithDate());

Foo는 다음과 같아야합니다.

class Foo {
    Date date;
    DateTime created_at;
}


답변

Gson은 하나의 datetime 형식 (빌더에 지정된 형식)과 사용자 지정 형식으로 구문 분석 할 수없는 경우 iso8601 만 처리 할 수 ​​있습니다. 따라서 해결책은 사용자 지정 deserializer를 작성하는 것입니다. 귀하의 문제를 해결하기 위해 정의했습니다.

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 + "]";
    }

}

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

}

마지막 단계는 Gson올바른 어댑터 로 인스턴스 를 만드는 것입니다 .

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

}

내 결과 :

Result: Foo [date=Tue Jul 16 00:00:00 CEST 2013, created_at=Tue Jul 16 22:52:36 CEST 2013]


답변

문자 그대로 만들려는 클래스에 “created_at”라는 이름의 Date 객체가 이미있는 경우 다음과 같이 쉽습니다.

Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").create();
YourObject parsedObject1 = gson.fromJson(JsonStringYouGotSomehow, YourObject.class);

그리고 당신은 끝났습니다. 복잡한 재정의가 필요하지 않습니다.


답변

다음과 같이 두 개의 새 클래스를 정의 할 수 있습니다.

import java.util.Date;

public class MyDate extends Date {
}

import java.util.Date;

public class CreatedAtDate extends Date {
}

POJO는 다음과 같습니다.

import MyDate;
import CreatedAtDate;

public class Foo {
    MyDate date;
    CreatedAtDate created_at;
}

마지막으로 사용자 지정 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;
    }
}

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MyDate.class, new MyDateDeserializer());


답변

이것은 질문에 대한 직접적인 대답은 아니지만, 코더가 문제를 해결하는 방법에 대한 완전한 선택의 자유를 가지고 있다면 “최신 기술”이라고 생각합니다.

우선, java.util.Date를 사용하는 것이 최선의 해결책이 아닙니다. 이유는 이러한 클래스가 일부 코너 케이스에서 이상적인 동작이 없었기 때문에 Java Instant 클래스 등으로 감독되는 경우이 SO 질문에서 Basil Bourque의 답변을 확인하십시오. API 레벨이 16 이하인 경우 Kotlin에서 Date 객체 만들기

그래서 저는 Android에서 ThreeTenABP의 Instant 클래스와 Kotlin을 사용했습니다.

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()