Fragment랑 같이 쓰려다가 MainFragment 예시 때문에 글이 너무 길어져서.
우선 response가 제대로 돌아오지 않던 이유를 찾았다. Retrofit
이 Gson
을 디폴트로 사용하기 때문에, 내가 Body로 전송하는 인스턴스가 Json
으로 자동으로 serialized 되었기 때문.
즉, 이미 serialized 된 JSONObject를 한 번 더 toJson()
시켜주는 삽질을 하고 있던 거다.
하여, 다시 만든 인터페이스.
LogInApi.kt
interface LogInApi {
@POST("/Login")
fun startLogin(@Body jsonObj: Operator) : Call<ApiResult>
companion object {
fun create() : ApiInterface {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(StaticValue.baseUrl)
.build()
return retrofit.create(LogInApi::class.java)
}
}
}
ActivityLogIn.kt
private fun startLogIn(objInfo: Obj) {
val apiInterface = LogInApi.create().startLogin(opInfo)
apiInterface.enqueue(object : Callback<ApiResult> {
override fun onResponse(call: Call<ApiResult>, response: Response<ApiResult>?) {
// 여기서 디버깅 포인트 찍어서 data 관측 중
}
override fun onFailure(call: Call<JsonObjBase>, t: Throwable) {
TODO("Not yet implemented")
}
})
}
ApiResult
의 "Data"가 JSONObject 형태로 들어올 때를 우선 가정해서 (이걸 넘겨야 그 다음을 하니까) 아래와 같이 만들었었다.
data class JsonObjBase (
@SerializedName("ResultCode") val resultCode : Int,
@SerializedName("ResultValue") val resultValue : String,
@SerializedName("ResultMessage") val resultMessage : String,
@SerializedName("Data") val data : JSONObject
)
문제는 Data의 정상적인 파싱 및 DTO로의 이식이 안 된 것.
StackoverFlow에 질문도 올리고 검색도 이리저리 해보다가 결국 Any
를 사용하기로 했다. 그래, 어차피 "Data"는 문자로도 JSONObject로도 JSONArray로도 들어오는데 다 대응해야지... 하는 김에 제대로 해야지 하고. (아님)
하여 완성판(?) ApiResult
ApiResult.kt
data class ApiResult(
@field:SerializedName("Data")
val data: Any? = null,
@field:SerializedName("ResultValue")
val resultValue: String? = null,
@field:SerializedName("ResultMessage")
val resultMessage: String? = null,
@field:SerializedName("ResultCode")
val resultCode: Int? = null
)
"Data"로 들어오는 모든 값들의 key들을 넣어두고 DTO에서는 try/catch를 활용하여 자기 것만 취하는 식으로 구조를 짰다. 더 좋은 방법이 있을 텐데... 당장은 떠오르지가 않는다. ㅠㅠ
ResponseData.kt
data class ResponseData(
@field:SerializedName("UserName")
val userName: String? = null,
@field:SerializedName("Enabled")
val enabled: Int? = null,
@field:SerializedName("UserId")
val userId: String? = null,
@field:SerializedName("Permission")
val permission: Long? = null,
...
)
이제는 "Data"를 상황에 맞추어 제대로 가져올 수 있다. 적어도 위에서 말한 세 가지 케이스에는 모두 성공한다!
다만 주의할 점은 "Data"의 JSONArray elements를 asJsonObject
로 변환하면 JSONObject가 아니라 JsonObject로 바뀐다. (아니 둘 차이 처음에 알지도 못했어요 코드로 잘 안 보여서 망할 case sensitive)
따라서 Data Class에서 이를 변환시켜줄 때는 JSONObj[foo] as String
이런 식이 아니라 JSONObj[foo].asString
으로 받아야 한다는 걸 기억해야 한다.
각각의 case에 대한 샘플 코드
// JSONObject
val type = object : TypeToken<MyClass>(){}.type
val g = Gson()
val result = g.toJson(response.body()!!.data)
val resultMyClass = MyClass(JSONObject(result))
// JSONArray
val type = object : TypeToken<MutableList<MyClass2>>(){}.type
val g = Gson()
val result = g.toJsonTree(response.body()!!.data, type)
val resultMyClass2 = result.asJsonArray
val myClass2List = ArrayList<MyClass2>()
if (resultMyClass2.size() > 0) {
for (i in 0 until resultMyClass2.size()) {
myClass2List.add(MyClass2(resultMyClass2[i].asJsonObject)
}
}
// String
val g = Gson()
val result = g.toJson(response.body()!!.data)
val resultStr = result.toString()
'개발일기' 카테고리의 다른 글
8월 한달 토이 프로젝트를 해보자! -02 : Navigation (0) | 2022.08.08 |
---|---|
8월 한달 토이 프로젝트를 해보자! -01 (0) | 2022.08.06 |
Retrofit - MVVM - Coroutine ㄲㄲㄲ (0) | 2021.09.15 |
MVVM 모델로 나아가기 1단계 : Fragment (0) | 2021.09.13 |
아이고 다 실패했다! : Retrofit (0) | 2021.09.10 |