这太长了,所以我不建议您按原样使用它。
只是为了向您展示使用 NSXMLParser 有时会像这样一团糟。
import Foundation
let url = NSBundle.mainBundle().URLForResource("complex", withExtension: "xml")!
protocol ElementParserType: class {
func startElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String],
in parserController: MyXMLParserController)
func endElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
in parserController: MyXMLParserController)
func foundCharacters(string: String, in parserController: MyXMLParserController)
}
class HeaderCol {
var label: String = ""
}
class Header {
var cols: [HeaderCol] = []
}
class Row {
var cols: [String] = []
}
class Body {
var rows: [Row] = []
}
class Result {
var header: Header?
var body: Body?
}
class LabelParser: ElementParserType {
var label: String?
let parent: HeaderColParser
init(parent: HeaderColParser) {
self.parent = parent
}
func startElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String],
in parserController: MyXMLParserController)
{
parserController.didFail("Invalid start element `\(elementName)` in label")
}
func endElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
in parserController: MyXMLParserController)
{
guard elementName == "label" else {
parserController.didFail("Invalid end element `\(elementName)` in label")
return
}
parent.col.label = self.label ?? ""
parserController.popParser()
}
func foundCharacters(string: String,
in parserController: MyXMLParserController)
{
self.label = string
}
}
class HeaderColParser: ElementParserType {
let col = HeaderCol()
let parent: HeaderParser
init(parent: HeaderParser) {
self.parent = parent
}
func startElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String],
in parserController: MyXMLParserController)
{
switch elementName {
case "label":
let labelParser = LabelParser(parent: self)
parserController.pushParser(labelParser)
default:
parserController.didFail("Invalid start element `\(elementName)` in col")
}
}
func endElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
in parserController: MyXMLParserController)
{
guard elementName == "col" else {
parserController.didFail("Invalid end element `\(elementName)` in col")
return
}
parent.header.cols.append(self.col)
parserController.popParser()
}
func foundCharacters(string: String,
in parserController: MyXMLParserController)
{
if string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).isEmpty {
parserController.didFail("Invalid characters '\(string)' in col")
}
}
}
class HeaderParser: ElementParserType {
let header = Header()
let parent: ResultParser
init(parent: ResultParser) {
self.parent = parent
}
func startElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String],
in parserController: MyXMLParserController)
{
switch elementName {
case "col":
let headerColParser = HeaderColParser(parent: self)
parserController.pushParser(headerColParser)
default:
parserController.didFail("Invalid start element `\(elementName)` in header")
}
}
func endElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
in parserController: MyXMLParserController)
{
guard elementName == "header" else {
parserController.didFail("Invalid end element `\(elementName)` in header")
return
}
parent.result.header = self.header
parserController.popParser()
}
func foundCharacters(string: String,
in parserController: MyXMLParserController)
{
if string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).isEmpty {
parserController.didFail("Invalid characters '\(string)' in header")
}
}
}
class RowColParser: ElementParserType {
var col: String?
let parent: RowParser
init(parent: RowParser) {
self.parent = parent
}
func startElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String],
in parserController: MyXMLParserController)
{
parserController.didFail("Invalid start element `\(elementName)` in col")
}
func endElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
in parserController: MyXMLParserController)
{
guard elementName == "col" else {
parserController.didFail("Invalid end element `\(elementName)` in col")
return
}
parent.row.cols.append(self.col ?? "")
parserController.popParser()
}
func foundCharacters(string: String,
in parserController: MyXMLParserController)
{
self.col = string
}
}
class RowParser: ElementParserType {
let row = Row()
let parent: BodyParser
init(parent: BodyParser) {
self.parent = parent
}
func startElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String],
in parserController: MyXMLParserController)
{
switch elementName {
case "col":
let rowColParser = RowColParser(parent: self)
parserController.pushParser(rowColParser)
default:
parserController.didFail("Invalid start element `\(elementName)` in row")
}
}
func endElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
in parserController: MyXMLParserController)
{
guard elementName == "row" else {
parserController.didFail("Invalid end element `\(elementName)` in row")
return
}
parent.body.rows.append(self.row)
parserController.popParser()
}
func foundCharacters(string: String,
in parserController: MyXMLParserController)
{
if string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).isEmpty {
parserController.didFail("Invalid characters '\(string)' in row")
}
}
}
class BodyParser: ElementParserType {
let body = Body()
let parent: ResultParser
init(parent: ResultParser) {
self.parent = parent
}
func startElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String],
in parserController: MyXMLParserController)
{
switch elementName {
case "row":
let rowParser = RowParser(parent: self)
parserController.pushParser(rowParser)
default:
parserController.didFail("Invalid start element `\(elementName)` in body")
}
}
func endElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
in parserController: MyXMLParserController)
{
guard elementName == "body" else {
parserController.didFail("Invalid end element `\(elementName)` in body")
return
}
parent.result.body = self.body
parserController.popParser()
}
func foundCharacters(string: String,
in parserController: MyXMLParserController)
{
if string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).isEmpty {
parserController.didFail("Invalid characters '\(string)' in footer")
}
}
}
class FooterParser: ElementParserType {
let parent: ResultParser
init(parent: ResultParser) {
self.parent = parent
}
func startElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String],
in parserController: MyXMLParserController)
{
switch elementName {
default:
parserController.didFail("Invalid start element `\(elementName)` in footer")
}
}
func endElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
in parserController: MyXMLParserController)
{
guard elementName == "footer" else {
parserController.didFail("Invalid end element `\(elementName)` in footer")
return
}
//Do nothing
parserController.popParser()
}
func foundCharacters(string: String,
in parserController: MyXMLParserController)
{
if string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).isEmpty {
parserController.didFail("Invalid characters '\(string)' in footer")
}
}
}
class ResultParser: ElementParserType {
let result = Result()
func startElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String],
in parserController: MyXMLParserController)
{
switch elementName {
case "header":
let headerParser = HeaderParser(parent: self)
parserController.pushParser(headerParser)
case "body":
let headerParser = BodyParser(parent: self)
parserController.pushParser(headerParser)
case "footer":
let headerParser = FooterParser(parent: self)
parserController.pushParser(headerParser)
default:
parserController.didFail("Invalid start element `\(elementName)` in result")
}
}
func endElement(
elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
in parserController: MyXMLParserController)
{
guard elementName == "result" else {
parserController.didFail("Invalid end element `\(elementName)` in result")
return
}
parserController.result = self.result
parserController.popParser()
}
func foundCharacters(string: String,
in parserController: MyXMLParserController)
{
if string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).isEmpty {
parserController.didFail("Invalid characters '\(string)' in footer")
}
}
}
class MyXMLParserController: NSObject, NSXMLParserDelegate {
let xmlParser: NSXMLParser
var result: AnyObject?
var parserStack: [ElementParserType] = []
var currentParser: ElementParserType? {
return parserStack.last
}
init?(url: NSURL) {
if let xmlParser = NSXMLParser(contentsOfURL: url) {
self.xmlParser = xmlParser
super.init()
self.xmlParser.delegate = self //The delegate is not retained.
} else {
return nil
}
}
func pushParser(parser: ElementParserType) {
parserStack.append(parser)
}
func popParser() {
parserStack.removeLast()
}
func parse() {
self.xmlParser.parse()
}
func parserDidStartDocument(parser: NSXMLParser) {
print(#function)
}
func parserDidEndDocument(parser: NSXMLParser) {
print(#function)
}
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
print(#function, elementName)
if let parser = currentParser {
parser.startElement(elementName,
namespaceURI: namespaceURI,
qualifiedName: qName,
attributes: attributeDict,
in: self)
} else {
guard elementName == "result" else {
print("Root element needs to be `result`")
parser.abortParsing()
return
}
pushParser(ResultParser())
}
}
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if let parser = currentParser {
parser.endElement(elementName,
namespaceURI: namespaceURI,
qualifiedName: qName,
in: self)
} else {
didFail("Invalid end element \(elementName) at top level")
}
}
func parser(parser: NSXMLParser, foundCharacters string: String) {
if let parser = currentParser {
parser.foundCharacters(string,
in: self)
} else {
didFail("Invalid characters '\(string)' at top level")
}
}
func didFail(error: String) {
xmlParser.abortParsing()
}
}
let parserController = MyXMLParserController(url: url)!
parserController.parse()
let result = parserController.result as! Result
if let header = result.header, body = result.body {
var rowArray: [[String: String]] = []
for row in body.rows {
assert(header.cols.count == row.cols.count)
var rowDict: [String: String] = [:]
for i in 0..<header.cols.count {
rowDict[header.cols[i].label] = row.cols[i]
}
rowArray.append(rowDict)
}
print(rowArray as NSArray)
} else {
print("header or body is missing")
}