공공데이터 포털에서 Json이 아닌 xml 을 응답으로 받아야 하는 상황이라서 xml 을 파싱하는 방법을 찾다가 처음에는 TikXml 을 사용 했으며, TikXml 사용시 gradle 충돌 문제가 있어서 다른 방법을 찾아봤다.
//빌드에러
Caused by: org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration$ArtifactResolveException: Could not resolve all files for configuration ':app:debugCompileClasspath'.
jaxb 로 대체하려 했으나 몇 시간의 삽질 끝에 android 지원하지 않는 것 알게됨
시도 해본 것
- annotaion 버전 down grade
- 캐시 삭제
- 리빌드 프로젝트 -> Could not find com.tickaroo.tikxml:annotation:0.8.15.
Required by:
project :app
project :app > com.tickaroo.tikxml:retrofit-converter:0.8.15 에러
- kapt 제거
- jaxb 라이브러리 사용 -> 안드로이드 지원x 삽질o
결국 버전을 내려서 다시 시도
implementation("com.tickaroo.tikxml:annotation:0.8.13")
implementation("com.tickaroo.tikxml:core:0.8.13")
kapt("com.tickaroo.tikxml:processor:0.8.13")
implementation("com.tickaroo.tikxml:retrofit-converter:0.8.13")
kapt("com.tickaroo.tikxml:auto-value-tikxml:0.8.13")
버전을 0.8.15 에서 0.8.13으로 내려서 그래들 문제는 해결이 되었는데 데이터가 들어온 이후에 타임아웃 에러가 뜸
-> 데이터 클래스 구조와 어노테이션을 잘 못 사용한 것 때문에 파싱이 제대로 안된 것 데이터 클래스 구조와 어노테이션을 제대로 사용해서 파싱 성공
KopisApi
package com.nbc.curtaincall.data.model
import retrofit2.http.GET
import retrofit2.http.Query
interface KopisApi {
@GET("pblprfr")
suspend fun fetchShowList(
@Query("stdate") stdate: String = "20240101",
@Query("eddate") eddate: String = "20240630",
@Query("cpage") cpage: String = "1",
@Query("rows") rows: String = "10",
): ShowListResponse
}
RetrofitClient
package com.nbc.curtaincall.data.api
import com.nbc.curtaincall.BuildConfig
import com.nbc.curtaincall.data.model.KopisApi
import com.tickaroo.tikxml.TikXml
import com.tickaroo.tikxml.retrofit.TikXmlConverterFactory
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
private const val BASE_URL = "http://kopis.or.kr/openApi/restful/"
private const val KOPIS_API_KEY = BuildConfig.KOPIS_API_KEY
object RetrofitClient {
private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY // 로깅 레벨 설정 (BASIC, HEADERS, BODY)
}
// API Key 삽입을 위한 인터셉터
private val apiKeyInterceptor = Interceptor { chain ->
val original = chain.request()
val originalHttpUrl = original.url
val url = originalHttpUrl.newBuilder()
.addQueryParameter("service", KOPIS_API_KEY) // Kopis api key 기본 추가
.build()
val requestBuilder = original.newBuilder().url(url)
val request = requestBuilder.build()
chain.proceed(request)
}
// OkHttpClient 설정
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor(apiKeyInterceptor)
.addInterceptor(loggingInterceptor)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.connectTimeout(15, TimeUnit.SECONDS)
.build()
// Retrofit 설정
private val retrofit by lazy {
//exceptionOnUnreadXml(false).build() 는 원하지 않는 데이터는 제외하기 위해서입니다. 내려받은 데이터를 모두 사용하신다면 추가하지 않아도 됩니다.
val parser = TikXml.Builder().exceptionOnUnreadXml(false).build()
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(TikXmlConverterFactory.create(parser))
.build()
}
val kopisApi: KopisApi by lazy { retrofit.create(KopisApi::class.java) }
}
ShowListResponse
package com.nbc.curtaincall.data.model
import com.tickaroo.tikxml.annotation.Element
import com.tickaroo.tikxml.annotation.PropertyElement
import com.tickaroo.tikxml.annotation.Xml
@Xml(name = "dbs")
data class ShowListResponse(
@Element(name = "db")
val showList: List<ShowList>
)
@Xml(name = "db")
data class ShowList(
@PropertyElement(name = "area") val area: String?,
@PropertyElement(name = "fcltynm") val fcltynm: String?,
@PropertyElement(name = "genrenm") val genrenm: String?,
@PropertyElement(name = "mt20id") val mt20id: String?,
@PropertyElement(name = "openrun") val openrun: String?,
@PropertyElement(name = "poster") val poster: String?,
@PropertyElement(name = "prfnm") val prfnm: String?,
@PropertyElement(name = "prfpdfrom") val prfpdfrom: String?,
@PropertyElement(name = "prfpdto") val prfpdto: String?,
@PropertyElement(name = "prfstate") val prfstate: String?
)
@PropertyElement는 nest child element가 없는 경우 @Element는 nest child element를 가진 경우
어노테이션 부분은 Json을 파싱할 때랑 비슷하다.
공식문서 참고
https://github.com/Tickaroo/tikxml/blob/master/docs/AnnotatingModelClasses.md
받아오는 XML 형태
<?xml version="1.0" encoding="UTF-8"?>
<dbs>
<db>
<mt20id>PF236104</mt20id>
<prfnm>싱어게인3 TOP10 전국투어 [울산]</prfnm>
<prfpdfrom>2024.06.08</prfpdfrom>
<prfpdto>2024.06.08</prfpdto>
<fcltynm>KBS홀 [울산]</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236104_240223_151315.gif</poster>
<genrenm>대중음악</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
<db>
<mt20id>PF236099</mt20id>
<prfnm>화통콘서트 [거창]</prfnm>
<prfpdfrom>2024.03.09</prfpdfrom>
<prfpdto>2024.03.09</prfpdto>
<fcltynm>거창문화센터</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236099_240223_144636.jpg</poster>
<genrenm>복합</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
<db>
<mt20id>PF236098</mt20id>
<prfnm>한경arte필하모닉 더클래식 시리즈3, 지휘 이병욱 & 바이올린 윤소영</prfnm>
<prfpdfrom>2024.03.28</prfpdfrom>
<prfpdto>2024.03.28</prfpdto>
<fcltynm>롯데콘서트홀</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236098_240223_143315.jpg</poster>
<genrenm>서양음악(클래식)</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
<db>
<mt20id>PF236097</mt20id>
<prfnm>퍼커셔니스트 고길영 & 홍진영 세 번째 듀오 리사이틀</prfnm>
<prfpdfrom>2024.03.31</prfpdfrom>
<prfpdto>2024.03.31</prfpdto>
<fcltynm>대전예술의전당</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236097_240223_142644.gif</poster>
<genrenm>서양음악(클래식)</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
<db>
<mt20id>PF236096</mt20id>
<prfnm>코리아아르츠그룹 베스트 아티스트 시리즈, 1. The Best Sopranos</prfnm>
<prfpdfrom>2024.03.11</prfpdfrom>
<prfpdto>2024.03.11</prfpdto>
<fcltynm>롯데콘서트홀</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236096_240223_141522.jpg</poster>
<genrenm>서양음악(클래식)</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
<db>
<mt20id>PF236095</mt20id>
<prfnm>지브리 페스티벌</prfnm>
<prfpdfrom>2024.05.12</prfpdfrom>
<prfpdto>2024.05.12</prfpdto>
<fcltynm>롯데콘서트홀</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236095_240223_140619.gif</poster>
<genrenm>서양음악(클래식)</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
<db>
<mt20id>PF236094</mt20id>
<prfnm>지브리 봄 음악 대축전</prfnm>
<prfpdfrom>2024.04.18</prfpdfrom>
<prfpdto>2024.04.18</prfpdto>
<fcltynm>롯데콘서트홀</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236094_240223_140210.jpg</poster>
<genrenm>서양음악(클래식)</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
<db>
<mt20id>PF236093</mt20id>
<prfnm>오페라속 여인들1 [제주 서귀포]</prfnm>
<prfpdfrom>2024.03.30</prfpdfrom>
<prfpdto>2024.03.30</prfpdto>
<fcltynm>서귀포예술의전당</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236093_240223_135545.jpg</poster>
<genrenm>서양음악(클래식)</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
<db>
<mt20id>PF236090</mt20id>
<prfnm>프린세스 공주뮤지컬쇼 [청주]</prfnm>
<prfpdfrom>2024.04.14</prfpdfrom>
<prfpdto>2024.04.14</prfpdto>
<fcltynm>청주예술의전당</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236090_240223_133927.jpg</poster>
<genrenm>뮤지컬</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
<db>
<mt20id>PF236088</mt20id>
<prfnm>기형도 35주기 추모 행사, 기형도 플레이</prfnm>
<prfpdfrom>2024.03.22</prfpdfrom>
<prfpdto>2024.03.22</prfpdto>
<fcltynm>광명시민회관</fcltynm>
<poster>http://www.kopis.or.kr/upload/pfmPoster/PF_PF236088_240223_132953.jpg</poster>
<genrenm>연극</genrenm>
<openrun>N</openrun>
<prfstate>공연예정</prfstate>
</db>
</dbs>
API 통신응답을 JSON으로 밖에 받아 본 경험 밖에 없어서 XML 을 파싱하는데 많은 시행착오와 씨뻘건 오류 메세지만 이틀간 보면서 씨름 했다. 결국 Json이랑 다를 건 별로 없는데, Xml을 파싱하는 과정이 순조롭지 못 했고 TikXml이 2020년에 commit 된게 마지막이라고 한다.
안드로이드에서는 XmlPullParser 사용을 추천하는 것 같은데 Retrofit을 사용하지 않는 것 같아서 다음에 한 번 알아봐야 겠다.