이 포스팅은 <SNS 앱을 만들면서 배우는 안드로이드 클라이언트 개발>, 장성환, 비제이퍼블릭(2022)을 읽고 개인 학습용으로 정리한 글입니다.
JSON(JavaScript Object Notion): 다양한 구조를 표현할 수 있는 데이터 포맷
JSON에서 사용할 수 있는 자료형:
날짜와 메세지를 포함하는 응답을 JSON 포맷으로 만들면: date와 message를 키로 갖는 오브젝트
{
"date": "2021-04-12T04:54:08Z,
"message": "Hello, world!"
}
conn.setRequestProperty("Accept", "application/json")
val json = JSONObject(body)
val date = json.getString("date")
val message = json.getString("message")
dependencies {
...
implementation 'com.google.code.gson:gson:2.8.8'
...
}
API 응답에 사용할 클래스들을 모아두기 위한 online.dailyq.api.response 패키지 생성
-> "Hello, world!" API의 응답을 정의한 클래스 생성 = Gson에서 사용할 모델
Gson에서 사용할 모델은 API 응답과 같은 구조, 이름, 타입을 가짐
package online.dailyq.api.response
data class HelloWorld (val date: String, val message: String)
val gson = Gson()
val helloWorld = gson.fromJson(body, HelloWorld::class.java)
activity?.runOnUiThread {
binding.date.text = helloWorld.date
binding.question.text = helloWorld.message
}
package online.dailyq.api.response
import java.util.Date
data class HelloWorld (val date: Date, val message: String)
Date: 시간을 담기 위한 객체
-> Date를 원하는 형식의 문자열로 만들기 위해서 DateFormat 사용
DateFormat의 getDateInstance(): 스타일과 로케일을 인자로 받아 그에 맞는 DateFormat 객체 생성
-> 로케일 Locale.KOREA일 때 스타일에 따라 다음의 형식 반환
val gson = Gson()
val helloWorld = gson.fromJson(body, HelloWorld::class.java)
val dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.KOREA)
activity?.runOnUiThread {
binding.date.text = dateFormat.format(helloWorld.date)
binding.question.text = helloWorld.message
}
일반적으로 코틀린/자바에서는 Camel case 사용
일반적으로 JSON에서는 Snake case 사용
기본적으로 JSON 속성의 이름과 클래스 속성의 이름이 같은 것으로 매핑
-> 서로 다른 문자열이 같은 것임을 알리기 위해 Gson 네이밍 정책 지정해야
Gson 객체는 생성 후 변경할 수 X
-> 생성하기 전에 GsonBuilder 사용해 원하는 설정해야
val gson = GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
Gson에서 JSON을 객체로 변환해야할 때
-> 기본 타입(ex. String, Int)은 별다른 설정 없이도 변환 O
-> 사용자가 직접 만든 클래스나 열거형(enum)은 변환 작업을 할 어댑터를 GsonBuilder에 등록해야
직렬화는 JsonSerializer 인터페이스를, 역직렬화는 JsonDeserializer 인터페이스를 구현
import com.google.gson.*
import java.lang.reflect.Type
enum class Build{
PRODUCT, RC, DEV
}
object BuildTypeAdapter : JsonSerializer<Build>, JsonDeserializer<Build> {
override fun serialize(
src: Build,
typeOfSrc: Type?,
context: JsonSerializationContext?
): JsonElement {
return JsonPrimitive(src.toString())
}
override fun deserialize(
json: JsonElement,
typeOfT: Type?,
context: JsonDeserializationContext?
): Build {
return Build.valueOf(json.asString)
}
}
val gson = GsonBuilder()
.registerTypeAdapter(Build::class.java, BuildTypeAdpater)
.create()
Gson의 기본 설정은 객체의 null인 멤버를 직렬화에서 생략
null인 멤버로 JSON 문자열로 직렬화해야할 때
-> GsonBuilder의 serializeNulls() 메서드
Gson은 효율성을 위해 JSON 문자열 개행/공백 생략
들여쓰기/개행 등 사람이 읽기 좋게 만들어야할 때
-> GonBuilder의 setPrettyPrinting() 메서드