[SWIFT] XML Parser 사용하기

2020. 9. 11. 16:18SWIFT

반응형

보통 서버와 통신을 하여 데이터를 받아올 때, JSON 타입을 많이 사용하지만,

아직 공공 데이터의 경우에는 XML의 형식도 있다고해요...‼️

 

저도 이번에 프로젝트를 진행하면서 한국 관광 공사의 데이터를 받아와야 했어요....😭

 

XML Parsing은 처음해보지만 JSON 형식과 크게 다른 건 없는 것 같더라구요.

 

 그럼 XML Parsing을 알아볼까요.


XML이란?

XML(eXtensible Markup Language) 이렇게 약자를 따와서 XML이라고 한대요..‼️

 

뭔가 이름의 뉘앙스만 봤을 때, 마크업 언어를 확대한다(?)

그런 의미인 것 같죠.

 

그렇다면 마크업 언어는 무엇일까요?

 

가장 대표적으로 여러분들이 가장 자주 봤을 HTML 언어가 있어요.

이 둘의 공통점은 무엇일까요?

 

바로 "마크(Mark)"로 둘러쌓여져 있다는 것입니다. 즉, 쉽게 말해서 태그로 둘러쌓여 행을 구분할 수 있습니다.

이렇듯 HTML, XML 두 언어는 보통 어떤 테를 잡을 때 자주 사용하죠.

더보기

Tip. 그럼 반대로 마크다운 언어는 무엇일까요..?

마크 다운 언어도 역시 마크업 언어의 일종이지만, 기본적으로 읽고 쓰기 쉽다는 점이 가장 큰 특징입니다.

대표적으로 .md 확장자가 있죠??

 

README.md 자주 쓰시죠 개발자라면 필수...

 

또한 페이스북의 태그 기능, 인스타의 해쉬태그 기능 등도 역시 마크다운 언어의 일종이래요...‼️

 

이렇게 소소하게 꿀팁으로 알아가세요~~

이런 형식을 굳이 사용하는 이유는 JSON을 사용하는 이유와 동일하다고 생각하면 될 것 같아요.

 

각 플랫폼마다 다루는 언어가 다르고 컴퓨터와 인간의 언어가 다르기 때문에, 이를 중간 지점의 타협점을 제공하기 위해 사용한다고 이해하면 될 것 같아요.

 

 

여기까지 서론이 너무 길었죠...⁉️


 

XML Parser 사용법

이제 대표적인 XML의 형식을 한 번 볼게요.

<body>
  <items>
    <item>
      <addr1>서울시청 광장(서울 중구 을지로 12)</addr1>
      <areacode>1</areacode>
      <cat1>A02</cat1>
    </item>
    <item>
      <addr1>수원시청</addr1>
      <areacode>1</areacode>
      <cat1>A02</cat1>
    </item>
  </items>
</body>

이렇게 각 <> 태그로 각 프로퍼티들이 둘러쌓여 있는게 보이시죠.

 

이제 여기서 파싱을 할 때, 각 태그로 구분을 하면 되겠죠??

 

그렇다면 우선 저 데이터를 받을 SWIFT 구조체를 먼저 만들어 볼게요.

struct items {
    let item: [item]
}

struct item {
    var addr1: String
    var areaCode: Int
    var cat1: String
}

 

이런 구조로 쉽게 만들 수 있겠죠. 

 

단, XML Parser로 데이터를 읽어올 때는 모두 String 타입으로 들어오기 때문에, 태그 별로 별도의 처리가 필요해요.

말로만 해서는 잘 모로르겠죠. 

 

iOS 프레임 워크에 있는 기능을 통해 XML Parsing 작업을 해볼게요.

 

기본 프레임 워크에 XMLParse라는 Class가 있어요.

어떤 XML 문서 파싱의 이벤트를 다루는 클래스이다.
(DTD는 저도 검색을 해봤는데, Document Type Definition이래요. 어떤 문서 타입에 대해서도 기능을 제공하는 것 같아요.)

 

XML Parsing의 기능을 제공해준다는 것 같죠?

 

그럼 바로 한 번 사용법에 대해 직접 코드를 볼게요.

func setParser(from url: URL, delegate: XMLDelegate) {
    let parser = XMLParser(contentsOf: url)
    parser.delegate = self
    parser.parse()
}

어떤 XML 데이터를 제공하는 URL로부터 XML을 읽어오고,

XMLParser의 Delegate가 어떤 Parsing의 이벤트를 처리해서 직접적인 파싱 작업을 하게됩니다.

 

델리게이트만 임명한다고 바로 이벤트가 시작하진 않겠죠⁉️

 

parser.parse()을 통해 Parsing 작업의 시작을 하면 됩니다.

 

그렇다면 Parsing은 시작했는데, 직접적인 데이터가 들어오는 부분이 안보이시죠.

이 이벤트들을 바로 델리게이트에서 처리를 하게 됩니다.

 

먼저 코드를 볼게요.

enum XMLKey: String {
    case addr1 = "addr1"
    case areaCode = "areaCode"
    case cat1 = "cat1"
}

class SampleVC {
    var items: [Item] = []
    var xmlDictionary: [String: String]?
    var crtElementType: XMLKey?
}

extension SampleVC: XMLParserDelegate {
    // XML 파싱을 시작하는 태그에서 이벤트 핸들링
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        if elementName == "item" { 
            xmlDictionary = [:]
        } else if elementName == "addr1" {
            crtElementType = .addr1
        } else if elementNmae == "areaCode" {
            crtElementType = .areaCode
        } else {
            crtElementType = .cat1
        }
    }
    
    // 태그의 Data가 string으로 들어옴
    func parser(_ parser: XMLParser, foundCharacters string: String) {
        guard xmlDictionary != nil,
            let crtElementType = self.crtElementType else { return }
       	xmlDictionary?.updateValue(string, crtElementType.rawValue)
    }
    
    // XML 파싱이 끝나는 태그에서 이벤트 핸들링
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        guard let xmlDictionary = self.xmlDictionary else { return } 
        if elementName == "item" {
            let item = Item()

            guard let areaCode = Int(xmlDictionary[XMLKey.areaCode.rawValue]),
                let addr1 = xmlDictionary[XMLKey.addr1.rawValue],
                let cat1 = xmlDictionary[XMLKey.cat1.rawValue] else { return } 
            item.areaCode = areaCode
            item.addr1 = addr1
            item.cat1 = cat1
            
            items.append(item)
            xmlDictionary = nil
        }
        
        crtElementType = nil
    }
}

 

1️⃣ 우선 Parsing을 시작해서 다루는 객체에서 Item의 시작 태그일 경우 현재 Item의 데이터를 입력받을 Dictionary을 생성했어요.

태그가 데이터 값에 대한 경우는 현재 데이터가 어떤 Key 값인지 알 수 있게 enum 타입을 할당해주었어요.

 

2️⃣태그의 값이 String으로 들어오는 경우에는 Dictionary의 key: value 타입으로 할당해주었어요.

 

그렇다면, 마지막으로 태그의 끝에는 어떤 작업을 하면 될까요?

 

3️⃣ 해당 Item을 만들어주고 Dictionary로부터 Item에 값을 할당해주고 Items에 붙여주고 다 쓴 값들은 nil로 정리해주었어요.

여기서 마지막에 값을 할당할 때, 하드 코딩을 하고 싶지 않으면 for문으로 Key 값에 따라 분기처리를 해주면 더욱 깔끔하게 할 수 있을 것 같아요.

 

 

이렇게하면 XML 데이터가 잘 Parsing이 되어서 들어오는 것을 확인하실 수 있어요..‼️

 

XML 파싱도 크게 어렵지 않죠..⁉️


참고 레퍼런스

반응형