일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- lazy 위험성
- Lifetime of SwiftUI
- consuming a restful web service
- TabBarItems
- ios
- lazy 사용
- Quick Help
- 적절한 사용방법
- git branch strategy
- frame이해
- ios login
- Your app is missing support for the following URL schemes: your_ios_client_id
- lifecycle
- swift
- Identity
- Demystify SwiftUI
- Xcode
- bounds이해
- Structural Identity
- native개발
- spring guide
- 보라색오류
- WWDC21
- xml delegate
- developer
- schemes
- view
- Explict Identity
- Lazy
- Spring
- Today
- Total
Dev_Dylan
[iOS] XML Parser, XML Parsing 하기 본문
Intro
먼저,
JSON은 JSONDecoder를 통해 훨씬 빠르고 간편하게 구현할 수 있다.
하지만 XML은 Parsing 보조 도구라고 생각하면 좋다.
XML에 태그 하나하나를 인식해주는 Delegate를 통해,
직접 Data DTO를 만들어 넣어주어야 한다.
XML 파싱의 원리
요소로 둘러쌓여 있기 때문에 HTML 을 이해한 사람들이라면 더 이해하기 쉬울 것
먼저 아래 사진에서 XML을 이해해보면,
1번은 가장바깥쪽 요소
2번은 두번째 요소
3번은 세번째 요소
…
더 있을 수 있고 없을 수 있다.
오른쪽과 같이 이루어져 있다.
- < > 여는 태그
- </ > 닫는 태그
즉 XMLParser
는 여는 태그와 닫는 태그를 찾아주는 메서드를 제공해준다.
그렇다면 우리는 일일이 어떤
태그에 대해 어떤 DTO를 뿌려줄 것인가에 대한 값만 넣어주면 된다.

XML파싱 하는 방법
직접 XMLCustomParser라는 타입을 만들어준다.
NSObject, XMLParserDelegate 채택 해주어야 한다.
XMLParserDelegete의 구현되어 있는 delegate를 제공받아 직접적으로 사용하면 된다.
전체 코드
class XMLCustomParser: NSObject, XMLParserDelegate {
private var currentElement: String?
// XML 데이터 파싱 메서드
private func parseXML(xmlData: Data) {
let parser = XMLParser(data: xmlData)
parser.delegate = self
parser.parse()
}
// 시작 태그를 만났을 때 호출
func parser(_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String] = [:]) {
currentElement = elementName
}
// 요소 내용을 만났을 때 호출
func parser(_ parser: XMLParser, foundCharacters string: String) {
switch currentElement {
case "pageIndex":
// 직접 DTO에 값 전달
break
case "totalCnt":
// 직접 DTO에 값 전달
break
default:
break
}
}
// 끝 태그를 만났을 때 호출
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "youthPolicy" {
// youthPolicyList.youthPolicy.append(currentYouth)
}
currentElement = nil
}
// 파싱 완료 시 호출
func parserDidEndDocument(_ parser: XMLParser) {
}
}
하나씩 살펴보자
currentElement는 현재 열린 태그가 어떤 요소인지를 담아주도록 만들어 주었는데,
추후에 해당요소에 어떤 값이 들어오는지 판단하기 위해 필요함
private var currentElement: String?
전체적으로 URLSession을 통해 XML Data를 받아온다면,
자동으로 파싱 해주도록 만든 메서드.
(없어도 되고 있어도 되고… 중요한건 delegate를 self 로 넣어주어야 한다.)
// XML 데이터 파싱 메서드
private func parseXML(xmlData: Data) {
let parser = XMLParser(data: xmlData)
parser.delegate = self
parser.parse() //parser를 실행하는 트리거
}
didStartElement
첫번째 메서드로, <>
와 같이 시작 태그를 만나면 호출해 준다.elementName
을 currentElement
에 담아주는 이유는 XMLParser가 데이터를 읽으면서
<태그> → 값 → </태그> 순서대로 읽어 내려가는데,
해당 태그가 열렸음을 알리고 데이터를 담아주어야 값이 읽혔을 때 어떤 태그인지 알려주기 위함이다.
// 시작 태그를 만났을 때 호출
func parser(_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String] = [:]) {
currentElement = elementName
}
foundCharacters
요소 내용 즉 태그사이의 값을 만날 때 호출되는 메서드이다.
string
이 요소 내용임
여기가 핵심
맞음..
노가다..
여러개 블로그 찾아도 어차피 다 함..
노가다 ……
원하는 값이 들어왔을 때 직접 데이터를 넣어주어야 함.
XML DTO를 구현했다면, 직접 넣어주면 됨.
// 요소 내용을 만났을 때 호출
func parser(_ parser: XMLParser, foundCharacters string: String) {
switch currentElement {
case "pageIndex":
// 직접 DTO에 값 전달
break
case "totalCnt":
// 직접 DTO에 값 전달
break
default:
break
}
}
시작 태그에서 currentElement를 담아주는 이유.
해당 태그에서 그 값을 DTO에 넣어주기 위해 switch문을 활용하여 직접 데이터를 넣어줌.
didEndElement
끝 태그를 만날때 호출되는 메서드
여러개의 중첩된 태그를 가지고 있을 때,
해당 태그가 끝날 때, 더이상 배열이 추가되지 않는다거나 할 때 사용할 수 있다.
// 끝 태그를 만났을 때 호출
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "youthPolicy" {
// youthPolicyList.youthPolicy.append(currentYouth)
}
currentElement = nil
}
parserDidEndDocument
파싱 완료 시 호출되는 메서드
데이터를 테스트하거나 필요시 호출하면 됨.
// 파싱 완료 시 호출
func parserDidEndDocument(_ parser: XMLParser) {
}
DTO구현
DTO에 담아주는 방법은 자유롭다.
아래와 같은 XML Data를 기준으로 생각하면
이 코드에서는 youthPolicy
가 반복된다.
<youthPolicyList>
<pageIndex>1</pageIndex>
<totalCnt>1273</totalCnt>
<youthPolicy>
<rnum>1</rnum>
</youthPolicy>
<youthPolicy>
<rnum>2</rnum>
</youthPolicy>
<youthPolicy>
<rnum>3</rnum>
</youthPolicy>
</youthPolicyList>
JSON과 같이 모든 경우의 수를 넣어서 DTO를 만들어줄 수 있다.
struct YouthDTO {
var youthPolicyList: YouthPolicyList
}
struct YouthPolicyList {
var pageIndex: String = ""
var totalCnt: String = ""
var youthPolicy: [YouthPolicy]
}
struct YouthPolicy {
var rnum: String = ""
}
DTO는 PostMan으로 값을 받아와 긁어 넣어 만들어 줄 수 있다
이렇게 구현된 DTO에 JSONDecoder처럼 넣어줄 수 있다면 얼마나 행복할까?
라는 꿈 깨고…
노가다 작업 해야하니 여러 글 찾아가면서
“다른거 없나??”
고민하지말고 그 시간에 구현하는게 빠름.
하지만 2개 이상의 중복된 데이터(”youthPolicy”)를 가져오게 된다면, 결국 위의 방식처럼 해야함
하지만 1개씩만 받아온다 하면, 단순히 didEndElement
를 호출할 필요 없이 값을 넣어주면 된다.
혹은, 아래와 같이 커스텀하게 rnum
값만 추출 할 수 있다.
struct DataDTO {
var rnum: String = ""
}
XML을 갑자기 사용하게 되었는데,
생각보다 받아오는 데이터가 많을 수록 시간이 들긴 하는 것 같음..
'iOS > iOS_Swift' 카테고리의 다른 글
[iOS] Lazy 키워드를 적절하게 사용하는 방법 (0) | 2024.01.13 |
---|---|
[iOS] Frame, Bounds 이해하기 (0) | 2023.12.31 |
[iOS] MVC 요약 (1) | 2023.12.08 |
Swift Basic _ Data Type (2) (0) | 2022.01.28 |
Swift Basic _ Data Type (1) (0) | 2022.01.28 |