Decodable 구조체만으로는 디코딩이 실패할 수 있어서,SingleOrArray 같은 래퍼 타입으로 대응할 수 있다.이번에 쓰는 API처럼 XML 응답을 받을 때 items 에서 item 구조가 달라지기도 한다
여러 건일 때
<items>
<item>...</item>
<item>...</item>
</items>
<items>
<item>...</item>
</items>
[PlantSummary], 어떤 경우에는 PlantSummary 처럼 해석될 수 있음struct PlantListItems: Decodable {
let item: [PlantSummary]
}
이 문제를 해결하기 위해 SingleOrArray<Element>를 사용했다.
struct SingleOrArray<Element: Decodable>: Decodable {
let values: [Element]
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let array = try? container.decode([Element].self) {
values = array
return
}
if let single = try? container.decode(Element.self) {
values = [single]
return
}
values = []
}
}
[Element]로 디코딩 시도Element로 디코딩 시도[single]로 감싸서 배열 형태로 변환[] 저장응답이 1개든 여러 개든 항상
[Element]형태로 통일해서 사용할 수 있게 해준다.
struct PlantListItems: Decodable {
let item: SingleOrArray<PlantSummary>?
}
let plants = response.response.body?.items?.item?.values ?? []
[PlantSummary]처럼 동일하게 사용할 수 있다.XML은 JSON보다 구조가 일정하지 않은 경우가 많아보인다..🫠🫠🫠 왜 Json이 널리 쓰이는지 알겠다..
특히 같은 태그가 상황에 따라 배열처럼 보일 때도 있고 단일 객체처럼 보일 때도 있어서 디코딩 로직을 조금 더 유연하게 만들어야 함!
SingleOrArray는 이런 문제를 해결해주는 역할을 한다.
SingleOrArray는 응답이 1개일 때도, 여러 개일 때도 항상 배열처럼 통일해서 다루기 위한 Decodable 래퍼
처음에는 item이 여러 개 들어오니까 그냥 [PlantSummary]로 받으면 될 것 같았는데 실제로는 응답 개수에 따라 구조가 달라질 수 있다는 것을 보고 삽질을 많이했다. 처음에는 지금껏 해왔던 단순 API 통신이니까 빠르게 끝날 것 같았는데 잘 API 구조를 세세하게 보지 않고 시작하다 보니 예상한거 보다 더 살짝 길어진거 같다..🥹🥹
국가 공공데이터의 경우 XML API를 많이 쓴다는데 지금 이런 예외 처리를 배워두면 나중에 도움이 되지 않을까 싶다. 아무튼 XML의 경우 비교적 유연하게 받아오는게 중요해서 단순히 모델을 만드는 것보다 응답 형태를 안정적으로 받아오는 구조를 설계하는 것이 중요하다는 걸 느꼈다.