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


81

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

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

Retrofit 또는 Gson에게 이러한 문자열을 변환하도록 어떻게 알릴 수 java.util.Date objects있습니까?



@Davmrtl : 여기에 두 가지 다른 날짜 형식이 있기 때문에 동일하지 않습니다. 따라서 다른 접근 방식이 필요합니다.
giampaolo

답변:


162
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의 응답을 살펴보십시오.


8
OP 및 다른 독자에게 더 유용하도록 코드를 간략하게 설명하십시오.
Mohit Jain 2014 년

모든 요청에 ​​대해 어떻게해야합니까? 그래서 그것을 내 ServiceGenerator?
Zapnologica

4
이것은 실제로 로케일을 적용하지 않습니다. 내 로케일 2016-02-11T13:42:14.401Z에 해당하는 날짜 객체로 역 직렬화 13:42:14됩니다.
Alireza Mirian

나는 이것을 시도했지만 다운로드 된 항목이 지금 작동하는 것 같습니다. 하지만 서버에 Json 객체를 게시 할 때. 그러면 내 시간대가 제거됩니까?
Zapnologica

이 솔루션을 구현 한 후 이유없이 앱 충돌 문제를 감지하는 데 많은 시간을 보냈습니다. 내가 복사 DETE 형식을 붙여 넣을 때 뭔가에서 인용 부호를 변경 'T'에 'T' -주의 시계 - 다른이!
LukaszTaraszka

86

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

2
날짜 형식을 복사 할 때 따옴표를 확인하십시오. 문제가 발생했습니다.
LukaszTaraszka

14

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

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

감사! 나는에서로만 변경 return df.parse(json.getAsString()); 해야했습니다 long timeStamp = Long.parseLong(json.getAsString()); return new java.util.Date(timeStamp);
Juan Saravia

큰! 감사합니다!
NickUnuchek

7

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]

1
Foo 안에 15-20 개의 필드 (사용자 지정 개체 포함)가있는 경우 수동으로 역 직렬화해야합니다. 그것은 Gson의 목적을 죽입니다.
파리 드

1

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

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

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


"이제 완료되었습니다. 복잡한 재정의가 필요하지 않습니다."전에 Retrofit으로 먼저 시도 했어야합니다.
Farid

1

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

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

0

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

우선, 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()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.