SwiftのXMLParser

(2019-11-24)

SwiftのXMLParserはイベント駆動のparser。

import UIKit

class ParserSample: NSObject {
    private let parser: XMLParser
    
    init(data: Data) {
        parser = XMLParser(data: data)
        super.init()
        parser.delegate = self
    }
    
    func parse() {
        guard parser.parse() else {
            guard let err = parser.parserError else {
                print("parse error but unknown reason")
                return
            }
            print("parse error: \(err.localizedDescription)")
            return
        }
        print("after parse()")
    }
}

XMLParserDelegateでイベントを拾ってオブジェクトに詰めるなりする。全て実装する必要はなく、この例ではタグの開始と文字列、CDATA、エラー、パース終了時の関数を実装している。

extension ParserSample: XMLParserDelegate {
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        print("parsing <\(elementName)>")
        if elementName == "ERROR" {
            self.parser.abortParsing() // test
        }
    }
    
    func parser(_ parser: XMLParser, foundCharacters string: String) {
        print("found \(string)")
    }
    
    func parser(_ parser: XMLParser,
                foundCDATA CDATABlock: Data) {
        let data = String(bytes: CDATABlock, encoding: .utf8)
        print("foundCDATA \(data!)")
    }
    
    func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
        print("parser error: \(parseError.localizedDescription)")
    }
    
    func parserDidEndDocument(_ parser: XMLParser) {
        print("parse done")
    }
}

最後まで正常にパースされる場合と、途中で中断される場合を実行する。

print("-- case 1 (success) --")

guard let data1 = "<A><B>\n<D>value</D></B><C><![CDATA[ccccc]]></C></A>".data(using: .utf8) else {
    print("cast to data1 error")
    exit(1)
}
ParserSample(data: data1).parse()

print("-- case 2 (abort) --")

guard let data2 = "<ERROR></ERROR>".data(using: .utf8) else {
    print("cast to data2 error")
    exit(1)
}
ParserSample(data: data2).parse()

最後までパースするとparserDidEndDocument()が実行された後parse()がtrueを返し、 途中で中断するとparseErrorOccurredの関数が呼ばれてparse()がfalseを返す。

-- case 1 (success) --
parsing <A>
parsing <B>
found 

parsing <D>
found value
parsing <C>
foundCDATA ccccc
parse done
after parse()
-- case 2 (abort) --
parsing <ERROR>
parser error: The operation couldn’t be completed. (NSXMLParserErrorDomain error 512.)
parse error: The operation couldn’t be completed. (NSXMLParserErrorDomain error 512.)