iyOmSd/Title: Swift

[Swift] XML 파싱

냄수 2020. 4. 3. 23:13
반응형

XML이란..?

Extensible Markup Language의 종류중 대표적인 언어에요

 

Markup Language 란?

태그, 등을 이용하여 문서나 데이터의 구조를 명기하는 언어의 한 가지라고 사전에 나오네요

<p>, <br>, <div> 이런 태그들 본적 있으시죠?? 그런 태그를 사용한 거에요

대표적으로 HTML, XML언어가 있죠

 

HTML 문자열로 응답을 받을 때 파싱받아서 앱에 적용 하고 싶은경우가 생길 수 있어요

RSS(Rich Site Summary)가 대표적인 예로 있겠네요!

RSS는 뉴스나 블로그 사이트에서 주로 사용하는 콘텐츠 표현 방식 이라고해요

 

아무튼!! 이러한 XML로 이루어진 응답을 파싱해보는 방법에 대해서 알아보려고 해요

 

예시로 구글뉴스의 RSS주소를 가져왔어요
아래의 주소를 치면 사진과같이 응답이 와요

 

https://news.google.com/rss?hl=ko&gl=KR&ceid=KR:ko

 

이러한 데이터중에서

원하는 데이터가 <title>태그에 있을 수 도있고, <link>태그에 있을 수 도있고

태그로 둘러쌓인 문자열만 골라서 가져와야겠죠??

 

 

이 작업을 편하게 할 수 있도록 Xcode에서 지원해주는게

바로 XMLParser이에요

 

제일 기본적인 XMLParser사용법을 알아볼게요

var parser: XMLParser
guard let url = URLComponents(string: rssURL)?.url else {
    return
}
parser = XMLParser(contentsOf: url)
parser.delegate = self
parser.parse()

URL타입의 매개변수를 넣어줘서 XMLParser객체를 생성하고

parse() 를 통해서

넣어준 URL의 응답을 파싱하기 시작해요

파싱받는 값은 

 

XMLParserDelegate 에 로직을 구현해야해요

 

// 태그의 시작
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:])

// 태그의 끝
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?)

// 태그사이의 문자열
func parser(_ parser: XMLParser, foundCharacters string: String)

// 에러시, abortParsing()사용시
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error)

 

<title>타이틀입니다</title>

 

이런 XML이 있다면

didStartElement elementName => title

didEndElement elementName => title

foundCharacters string => 타이틀입니다

 

이렇게 값을 받을 수 있어요

느낌이 오시나요??!

 

그리고 파싱을 그만두고싶다!?

abortParsing() 을 통해서 멈추게 할 수 있어요

이 함수를 실행 시키면

func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) delegate함수를 통해서

정보가 전달된다고 하네요

 

문자열을 가져올 때

주의할점이 있어요

xml태그끝에 빈칸을 인식하기때문에

데이터가 아닌 빈배열저장 되는 문제가 발생해요

 

(예시)

<country>한국</country>

에서 한국과 </country>뒤의 공백문자를인식해요

 

해결방법으로는

Bool형 변수 트리거 방식

혹은

trimmingCharacters(in: .whitespacesAndNewlines) 을 이용해서 앞뒤 공백을 제거할 수도 있어요

 

원하는 태그의 문자열을 가져오는 코드를 간단하게 작성해 볼게요

 

간략하게 우선 말로 설명하자면

item태그 안에 있는 title태그의 문자열을 가져올거에요

<item>

    <title> ... </title>

</item>

<item>

    <title> ... </title>

</item>

으로 이루어진 형식이구요!!

 

item태그가 시작되면

Rss객체를 만들어주고

title의 문자열을 넣어준뒤

item태그가 끝나면

배열에 추가해줄거에요

    enum TagType {
        case title, none
    }
    
    class Rss {
        var title: String
    }
    var isLock = false
    var tagType: TagType = .none
    var tempRssModel: Rss?
    var rssList: [Rss] = []
    
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        if elementName == "item" {
            isLock = true
            tempRssModel = Rss()
        } else if elementName == "title" {
            tagType = .title
        } else {
        	tagType = .none
        }
    }
    
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        if elementName == "item" {
            guard let tempRssModel = tempRssModel else {
                return
            }
            rssList.append(tempRssModel)
            isLock = false
        } else if elementName == "channel" {    // xml의 마지막태그 - channel
            parser.abortParsing()
            rssTableView.reloadData()
        }
    }
    
    func parser(_ parser: XMLParser, foundCharacters string: String) {
        if isLock {
            switch tagType {
            case .title:
                let parseString = string.trimmingCharacters(in: .whitespacesAndNewlines)
                tempRssModel.title = parseString
            }
    	}
    }

 

 

하다가 고민할 점이 생겼어요

XML파싱할 때 link에 있는 URL로 다시 XML파싱을 받아서 본문을 발췌해 오는 기능을 구현하는데 있어서

양이 많다보니 버벅거리는 현상이 발생했어요

 

저는 각 Cell안에서 link로 다시 XML을 파싱하도록 구현했는데 버벅이는 현상을 잡지 못해서

좋지 않은 방법 같다고 생각했어요

 

XML의 양이 많이질때의 문제를 생각해볼 필요가 있을거 같아요

반응형