Thursday, July 28, 2016

How to write custom XMLParser for Swift 3

There are some general purpose XMLParsers libraries on github e.g. SWXMLHash or XML

Sometimes, it is faster to write your own and can use it in Playgrounds. What you need for a simple XMLParser is to define a class with XMLParserDelegate and write parsing rules in "didStartElement", "foundCharacters" and "didEndElement" methods in the delegated class.

Below is an example code in swift 3 (Xcode 8 beta 6) and with Swift 2.1 code added as comments underneath. And try to parse the xml file from quandl.com online dataset.
// curl "https://www.quandl.com/api/v3/datasets/FRED/DEXJPUS.xml"

These codes do not work on Linux Swift due to incomplete implementation of Foundation library on Linux yet.

testQuandl_main.swift    Select all
// // main.swift // testQuandl // import Foundation func timetest(_ note: String, block: () -> Void) //func timetest(note: String, block: () -> Void) // Swift 2 { let date = Date() // let date = NSDate() // Swift 2 block() let timeInterval = Date().timeIntervalSince(date) // let timeInterval = NSDate().timeIntervalSinceDate(date) // Swift 2 print("Test:", note); print("Elapsed time: \(String(format: "%.2f", timeInterval)) s") } extension String { func trimmed() -> String { return self.trimmingCharacters(in: .whitespacesAndNewlines) //return self.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines()) // Xcode 8 beta 1 //return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) // Swift 2 } } class QuandlCCYParser : NSObject, Foundation.XMLParserDelegate { // class QuandlCCYParser : NSObject, Foundation.NSXMLParserDelegate { // Swift 2 var parser = Foundation.XMLParser() // var parser = Foundation.NSXMLParser() // Swift 2 fileprivate var element:String = String() fileprivate var type:String = String() fileprivate var elements:Dictionary<String,String>? = [String: String]() var dataset = [Dictionary<String,String>]() fileprivate var name:String? = String() fileprivate var newest_available_date:String? = String() var dataPoints:[Dictionary<String,String>] = [[String:String]]() fileprivate var date:String? = String() fileprivate var value:String? = String() func startParsing(_ xmltxt:String) { // func startParsing(xmltxt:String) { // Swift 2 dataset = [] let xmlData = xmltxt.data(using: String.Encoding.utf8)! // let xmlData = xmltxt.dataUsingEncoding(NSUTF8StringEncoding)! // Swift 2 parser = Foundation.XMLParser(data: xmlData) // parser = Foundation.NSXMLParser(data: xmlData) // Swift 2 parser.delegate = self parser.parse() } func startParsing(urlpath:String) { // func startParsing(urlpath urlpath:String) { // Swift 2 dataset = [] parser = Foundation.XMLParser(contentsOf:(Foundation.URL(string:urlpath))! as Foundation.URL)! // parser = Foundation.NSXMLParser(contentsOfURL:(Foundation.NSURL(string:urlpath))! as Foundation.NSURL)! // Swift 2 parser.delegate = self parser.parse() } func parser(_ parser: Foundation.XMLParser, // func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { element = elementName if let type = attributeDict["type"] { self.type = type } else { self.type = "" } if (elementName == "datum" && attributeDict["type"] == "date") { date = nil elements = nil } else if (elementName == "datum" && attributeDict["type"] == "float") { value = nil elements = nil } else if (elementName == "name") { elements = nil name = nil } else if (elementName == "newest-available-date" && attributeDict["type"] == "date") { elements = nil newest_available_date = nil } } func parser(_ parser: Foundation.XMLParser, // func parser(parser: Foundation.NSXMLParser, // Swift 2 foundCharacters string: String) { if element == "datum" && type == "date" { if date == nil { date = "" } date!.append(string) // date! += string // Swift 2 } else if element == "datum" && type == "float" { if value == nil { value = "" } value!.append(string) // value! += string // Swift 2 } else if element == "name" { if name == nil { name = "" } name!.append(string) // name! += string // Swift 2 } else if element == "newest-available-date" && type == "date" { if newest_available_date == nil { newest_available_date = "" } newest_available_date!.append(string) // newest_available_date! += string // Swift 2 } } func parser(_ parser: Foundation.XMLParser, // func parser(parser: Foundation.NSXMLParser, // Swift 2 didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { if (elementName == "datum" && type == "float") { if (date != nil && value != nil) { elements = [:] elements!["date"] = date!.trimmed() elements!["value"] = value!.trimmed() } if elements != nil { dataPoints.append(elements!) (date, value, elements) = (nil, nil, nil) } } else if elementName == "dataset" { elements = [:] if name != nil { elements!["name"] = name!.trimmed() } if newest_available_date != nil { elements!["newest-available-date"] = newest_available_date!.trimmed() } if elements != nil { dataset.append(elements!) } (date, value, elements) = (nil, nil, nil) } } func showdataset() { for data in dataset { if let name = data["name"], let newest_available_date = data["newest-available-date"] { // SE-0099 XCode 8 beta 4 // if let name = data["name"], newest_available_date = data["newest-available-date"] { // Swift 2 print("\(name) \(newest_available_date)") } } } } func test1() { let test = QuandlCCYParser() // curl "https://www.quandl.com/api/v3/datasets/FRED/DEXJPUS.xml" test.startParsing(urlpath:"https://www.quandl.com/api/v3/datasets/FRED/DEXJPUS.xml") test.showdataset() for data in test.dataPoints.reversed() where data["date"]! >= "2016-01-01" { // for data in test.dataPoints.reverse() where data["date"] >= "2016-01-01" { // Swift 2 if let date = data["date"], let value = Float(data["value"]!) { // SE-0099 XCode 8 beta 4 // if let date = data["date"], value = Float(data["value"]!) { // Swift 2 print("Date \(date) , USDJPY = \(String(format: "%.2f", value)) JPYUSD = \(1/value)") } } } timetest("DEXJPUS XMLParser", block:test1)

Xcode 8 Beta output




Below is another example XMLParser for Markit Interest Rate Curve Data and to retrieve curvepoints and other field items under Deposits and Swaps tags.
testMarkit_main.swift    Select all
// // main.swift // testMarkit // import Foundation func timetest(_ note: String, block: () -> Void) //func timetest(note: String, block: () -> Void) // Swift 2 { let date = Date() // let date = NSDate() // Swift 2 block() let timeInterval = Date().timeIntervalSince(date) // let timeInterval = NSDate().timeIntervalSinceDate(date) // Swift 2 print("Test:", note); print("Elapsed time: \(String(format: "%.2f", timeInterval)) s") } extension String { func trimmed() -> String { return self.trimmingCharacters(in: .whitespacesAndNewlines) //return self.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines()) // Xcode 8 beta 1 //return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) // Swift 2 } } class ParserBase : NSObject, Foundation.XMLParserDelegate { // class ParserBase : NSObject, Foundation.NSXMLParserDelegate { // Swift 2 var parser = Foundation.XMLParser() // var parser = Foundation.NSXMLParser() // Swift 2 var currentElement:String = "" var foundCharacters = "" weak var parent:ParserBase? = nil func startParsing(_ xmltxt:String) { // func startParsing(xmltxt:String) { // Swift 2 let xmlData = xmltxt.data(using: String.Encoding.utf8)! // let xmlData = xmltxt.dataUsingEncoding(NSUTF8StringEncoding) // Swift 2 parser = Foundation.XMLParser(data: xmlData) // parser = Foundation.NSXMLParser(data: xmlData) // Swift 2 parser.delegate = self parser.parse() } func startParsing(urlpath:String) { // func startParsing(urlpath urlpath:String) { // Swift 2 parser = Foundation.XMLParser(contentsOf:(Foundation.URL(string:urlpath))! as Foundation.URL)! // parser = Foundation.NSXMLParser(contentsOfURL:(Foundation.NSURL(string:urlpath))! as Foundation.NSURL)! // Swift 2 parser.delegate = self parser.parse() } func startParsing(url:URL) { // func startParsing(url url:Foundation.NSURL) { // Swift 2 parser = Foundation.XMLParser(contentsOf:url)! // parser = Foundation.NSXMLParser(contentsOfURL:url)! // Swift 2 parser.delegate = self parser.parse() } func parser(_ parser: Foundation.XMLParser, // func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { currentElement = elementName } func parser(_ parser: Foundation.XMLParser, foundCharacters string: String) // func parser(parser: Foundation.NSXMLParser, foundCharacters string: String) // Swift 2 { self.foundCharacters += string } } class InterestRateCurve : ParserBase { var currency = "" var effectiveasof = "" lazy var deposits = Deposit() lazy var swaps = Swap() override func parser(_ parser: Foundation.XMLParser, // override func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { // if we found a tag, delegate further responsibility // to parsing to a new instance of Deposit or Swap if elementName == "deposits" { self.deposits = Deposit() // push responsibility parser.delegate = deposits // let delegate know who is the parent // so that once done XML processing // it can return parsing responsibility back deposits.parent = self } else if elementName == "swaps" { self.swaps = Swap() // push responsibility parser.delegate = swaps // let delegate know who is the parent // so that once done XML processing // it can return parsing responsibility back swaps.parent = self } super.parser(parser, didStartElement: elementName, namespaceURI: namespaceURI, qualifiedName: qName, attributes: attributeDict) } func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // func parser(parser: Foundation.NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // Swift 2 { // get other elements for deposits if elementName == "currency" { self.currency = foundCharacters.trimmed() } else if elementName == "effectiveasof" { self.effectiveasof = foundCharacters.trimmed() } // reset found characters foundCharacters = "" } } class Deposit : ParserBase { var curvepoints = [CurvePoint]() var spotdate:String = "" var daycountconvention:String = "" var snaptime:String = "" override func parser(_ parser: Foundation.XMLParser, // override func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { if elementName == "curvepoint" { let depo = CurvePoint() self.curvepoints.append(depo) // push responsibility parser.delegate = depo // let delegate know who is the parent depo.parent = self } super.parser(parser, didStartElement: elementName, namespaceURI: namespaceURI, qualifiedName: qName, attributes: attributeDict) } func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // func parser(parser: Foundation.NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // Swift 2 { // print("processing didEndElement <\(elementName)> tag from Deposit") // if we reached the tag, we do not // have anything further to do, so delegate // parsing responsibility to parent if elementName == "deposits" { parser.delegate = self.parent } // get other field items for deposits else if elementName == "spotdate" { self.spotdate = foundCharacters.trimmed() } else if elementName == "daycountconvention" { self.daycountconvention = foundCharacters.trimmed() } else if elementName == "snaptime" { self.snaptime = foundCharacters.trimmed() } // reset found characters foundCharacters = "" } } class Swap : ParserBase { var curvepoints = [CurvePoint]() var spotdate:String = "" var fixeddaycountconvention:String = "" var floatingdaycountconvention:String = "" var fixedpaymentfrequency:String = "" var floatingpaymentfrequency:String = "" var snaptime:String = "" override func parser(_ parser: Foundation.XMLParser, // override func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { if elementName == "curvepoint" { let swap = CurvePoint() self.curvepoints.append(swap) // push responsibility parser.delegate = swap // let delegate know who is the parent // so that it can return parsing responsibility // back after XML processing is done swap.parent = self } super.parser(parser, didStartElement: elementName, namespaceURI: namespaceURI, qualifiedName: qName, attributes: attributeDict) } func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // func parser(parser: Foundation.NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // Swift 2 { // print("processing didEndElement <\(elementName)> tag from Swap") // if we reached the </swaps> tag, we do not // have anything further to do, so delegate // parsing responsibility to parent if elementName == "swaps" { parser.delegate = self.parent } // get other field items for swaps else if elementName == "spotdate" { self.spotdate = foundCharacters.trimmed() } else if elementName == "fixeddaycountconvention" { self.fixeddaycountconvention = foundCharacters.trimmed() } else if elementName == "floatingdaycountconvention" { self.floatingdaycountconvention = foundCharacters.trimmed() } else if elementName == "fixedpaymentfrequency" { self.fixedpaymentfrequency = foundCharacters.trimmed() } else if elementName == "floatingpaymentfrequency" { self.floatingpaymentfrequency = foundCharacters.trimmed() } else if elementName == "snaptime" { self.snaptime = foundCharacters.trimmed() } // reset found characters foundCharacters = "" } } class CurvePoint : ParserBase { var tenor = "" var maturitydate = "" var parrate:Float = 0.0 func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // func parser(parser: Foundation.NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // Swift 2 { // print("processing didEndElement <\(elementName)> tag from CurvePoint") // if we finished an tag, the ParserBase parent // would have accumulated the found characters // so just assign that to our variable if elementName == "tenor" { self.tenor = foundCharacters.trimmed() } else if elementName == "maturitydate" { self.maturitydate = foundCharacters.trimmed() } // similarly for other tags else if elementName == "parrate" { if let l = Float(foundCharacters.trimmed()) { self.parrate = l } } // if we reached the </curvepoint> tag, we do not // have anything further to do, so delegate // parsing responsibility to parent else if elementName == "curvepoint" { parser.delegate = self.parent } // reset found characters foundCharacters = "" } } func printcurve(_ xmltxt:String) -> Void { //func printcurve(xmltxt:String) -> Void { // Swift 2 let interestRateCurve = InterestRateCurve() interestRateCurve.startParsing(xmltxt) print("interestRateCurve currency is \(interestRateCurve.currency) and effectiveasof is \(interestRateCurve.effectiveasof)") print(String(repeating: "-", count: 65)) // SE-0130 Xcode 8 beta 6 // print(String(repeating: ("-" as Character), count: 65)) // Xcode 8 beta 1 print("Deposits spotdate is \(interestRateCurve.deposits.spotdate) daycountconvention is \(interestRateCurve.deposits.daycountconvention)") for depo in interestRateCurve.deposits.curvepoints { print("Depo tenor = \(depo.tenor) maturitydate = \(depo.maturitydate) and parrate = \(depo.parrate)") } print("") print("Swaps spotdate is \(interestRateCurve.swaps.spotdate) fixeddaycountconvention is \(interestRateCurve.swaps.fixeddaycountconvention) floatingdaycountconvention is \(interestRateCurve.swaps.floatingdaycountconvention)") for swap in interestRateCurve.swaps.curvepoints { print("Swap tenor = \(swap.tenor) maturitydate = \(swap.maturitydate) and parrate = \(swap.parrate)") } } let xmltxt = "<?xml version=\"1.0\" standalone=\"yes\" ?><interestRateCurve><effectiveasof>2016-07-05</effectiveasof><currency>GBP</currency><baddayconvention>M</baddayconvention><deposits><daycountconvention>ACT/365</daycountconvention><snaptime>2016-07-04T15:00:00.000Z</snaptime><spotdate>2016-07-05</spotdate><calendars><calendar>none</calendar></calendars><curvepoint><tenor>1M</tenor><maturitydate>2016-08-05</maturitydate><parrate>0.004848</parrate></curvepoint><curvepoint><tenor>2M</tenor><maturitydate>2016-09-05</maturitydate><parrate>0.005009</parrate></curvepoint><curvepoint><tenor>3M</tenor><maturitydate>2016-10-05</maturitydate><parrate>0.005163</parrate></curvepoint><curvepoint><tenor>6M</tenor><maturitydate>2017-01-05</maturitydate><parrate>0.006185</parrate></curvepoint><curvepoint><tenor>1Y</tenor><maturitydate>2017-07-05</maturitydate><parrate>0.008459</parrate></curvepoint></deposits><swaps><fixeddaycountconvention>ACT/365</fixeddaycountconvention><floatingdaycountconvention>ACT/365</floatingdaycountconvention><fixedpaymentfrequency>6M</fixedpaymentfrequency><floatingpaymentfrequency>6M</floatingpaymentfrequency><snaptime>2016-07-04T15:00:00.000Z</snaptime><spotdate>2016-07-05</spotdate><calendars><calendar>none</calendar></calendars><curvepoint><tenor>2Y</tenor><maturitydate>2018-07-05</maturitydate><parrate>0.00497</parrate></curvepoint><curvepoint><tenor>3Y</tenor><maturitydate>2019-07-05</maturitydate><parrate>0.00504</parrate></curvepoint><curvepoint><tenor>4Y</tenor><maturitydate>2020-07-05</maturitydate><parrate>0.00539</parrate></curvepoint><curvepoint><tenor>5Y</tenor><maturitydate>2021-07-05</maturitydate><parrate>0.00589</parrate></curvepoint><curvepoint><tenor>6Y</tenor><maturitydate>2022-07-05</maturitydate><parrate>0.00656</parrate></curvepoint><curvepoint><tenor>7Y</tenor><maturitydate>2023-07-05</maturitydate><parrate>0.00732</parrate></curvepoint><curvepoint><tenor>8Y</tenor><maturitydate>2024-07-05</maturitydate><parrate>0.00809</parrate></curvepoint><curvepoint><tenor>9Y</tenor><maturitydate>2025-07-05</maturitydate><parrate>0.00879</parrate></curvepoint><curvepoint><tenor>10Y</tenor><maturitydate>2026-07-05</maturitydate><parrate>0.0094</parrate></curvepoint><curvepoint><tenor>12Y</tenor><maturitydate>2028-07-05</maturitydate><parrate>0.01041</parrate></curvepoint><curvepoint><tenor>15Y</tenor><maturitydate>2031-07-05</maturitydate><parrate>0.01134</parrate></curvepoint><curvepoint><tenor>20Y</tenor><maturitydate>2036-07-05</maturitydate><parrate>0.01186</parrate></curvepoint><curvepoint><tenor>25Y</tenor><maturitydate>2041-07-05</maturitydate><parrate>0.01179</parrate></curvepoint><curvepoint><tenor>30Y</tenor><maturitydate>2046-07-05</maturitydate><parrate>0.01162</parrate></curvepoint></swaps></interestRateCurve>" timetest("Interest Rate Curve XMLParser", block:{printcurve(xmltxt)})

Xcode 8 Beta output




MarKit data are zipped archives. Here is a sample shell script to retrieve and unzip the interest rate curve data. Alternative way is to use other unzip Swift Framework and unzip the url data contents in the code.
getMakit.sh    Select all
#!/bin/bash mkdir -p ~/Downloads/MarKit cd ~/Downloads/MarKit for date in "20160704" "20160705" ;do for ccy in "USD" "GBP" "EUR" "JPY" "CHF" "CAD" "AUD" "NZD" "SGD" "HKD" ;do echo InterestRates_${ccy}_${date}.zip curl -OL https://www.markit.com/news/InterestRates_${ccy}_${date}.zip unzip -o InterestRates_${ccy}_${date}.zip done done


After download and unzip in a folder, it is possible to run a series of XMLParser like this code below

testMarkit2_main.swift    Select all
func test2(url:Foundation.URL) { //func test2(url url:Foundation.NSURL) { // Swift 2 let interestRateCurve = InterestRateCurve() interestRateCurve.startParsing(url:url) print("interestRateCurve currency is \(interestRateCurve.currency) and effectiveasof is \(interestRateCurve.effectiveasof)") print(String(repeating: "-", count: 65)) // SE-0130 Xcode 8 beta 6 // print(String(repeating: ("-" as Character), count: 65)) // Xcode 8 beta 1 print("Deposits spotdate is \(interestRateCurve.deposits.spotdate) daycountconvention is \(interestRateCurve.deposits.daycountconvention)") for depo in interestRateCurve.deposits.curvepoints { print("Depo tenor = \(depo.tenor) maturitydate = \(depo.maturitydate) and parrate = \(depo.parrate)") } print("") print("Swaps spotdate is \(interestRateCurve.swaps.spotdate) fixeddaycountconvention is \(interestRateCurve.swaps.fixeddaycountconvention) floatingdaycountconvention is \(interestRateCurve.swaps.floatingdaycountconvention)") for swap in interestRateCurve.swaps.curvepoints { print("Swap tenor = \(swap.tenor) maturitydate = \(swap.maturitydate) and parrate = \(swap.parrate)") } } for date in ["20160704","20160705"] { for ccy in ["USD","GBP"] { let url = Foundation.URL(fileURLWithPath: NSHomeDirectory() + "/Downloads/MarKit/InterestRates_\(ccy)_\(date).xml") // let url = Foundation.NSURL(fileURLWithPath: NSHomeDirectory() + "/Downloads/MarKit/InterestRates_\(ccy)_\(date).xml") // Swift 2 timetest("InterestRates_\(ccy)_\(date).xml", block:{test2(url:url)}) } }


Below is the playground code (swift 3 Xcode 8 beta 6) that make use of the compression library (introduced from iOS9 and macOS10.11) to unzip the MarKit Interest Rate Curve url download and then possibly use the InterestRateCurve() XMLParser to print curve points.

unzip_getMarkit.swift    Select all
import Foundation import Compression func timetest(_ note: String, block: () -> Void) //func timetest(note: String, block: () -> Void) // Swift 2 { let date = Date() // let date = NSDate() // Swift 2 block() let timeInterval = Date().timeIntervalSince(date) // let timeInterval = NSDate().timeIntervalSinceDate(date) // Swift 2 print("Test:", note); print("Elapsed time: \(String(format: "%.2f", timeInterval)) s") } func getMarkit(ccy:String, date:String) -> String? { //func getMarkit(ccy ccy:String, date:String) -> String? { // Swift 2 var bytes:[UInt8] = [] var dst:[UInt8] = [] let markitURL = Foundation.URL(string: "https://www.markit.com/news/InterestRates_"+ccy+"_"+date+".zip") // let markitURL = Foundation.NSURL(string: "https://www.markit.com/news/InterestRates_"+ccy+"_"+date+".zip") // Swift 2 guard let markitData = try? Data(contentsOf: markitURL!) , markitData.subdata(in: 0..<4).elementsEqual([0x50,0x4b,0x03,0x04]) // find <504b0304> little-endian <04034b50> // guard let markitData = try? Data(contentsOf: markitURL!) where markitData.subdata(in: 0..<4).elementsEqual([0x50,0x4b,0x03,0x04]) // Xcode 8 beta 1 // guard let markitData = NSData(contentsOfURL: markitURL!) where markitData.subdataWithRange(NSMakeRange(0,4)).isEqualToData(NSData(bytes:[0x50,0x4b,0x03,0x04] as [UInt8], length:4)) // Swift 2 else { return nil } // if let data:NSData = markitData as NSData { // Swift 2 or Xcode 8 beta 1 bytes = Array(UnsafeBufferPointer(start: (markitData as NSData).bytes.bindMemory(to: UInt8.self, capacity: markitData.count), count: markitData.count)) // SE-0107 Xcode 8 beta 6 and convert from markitData:Data to [UInt8] // if convert from data:NSData to [UInt8] // bytes = Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(data.bytes.assumingMemoryBound(to: UInt8.self)), count: data.length)) // Xcode 8 beta 6 // bytes = Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(data.bytes), count: data.length)) // Swift 2 or Xcode 8 beta 1 // } // Swift 2 or Xcode 8 beta 1 var firstcentralheader: Int = 0 var offset: Int = 0 var nextoffset: Int = 0 var found:Bool = false let xmlfilenameprefix = "InterestRates" var xmlfilenamelength: Int = 0 var xmlfilename : String = "" for i in 1...bytes.count-4 { // Central directory file header signature = 0x02014b50 if bytes[i..<i+4].elementsEqual([0x50,0x4b,0x01,0x02]) { // find <504b0102> little-endian <02014b50> if firstcentralheader == 0 { firstcentralheader = i } // Central directory : 28 2 File name length (n) let filenamelength = bytes[(i+28)..<(i+28+2)] .enumerated() // .enumerate() // Swift 2 .map { (index, element) in return Int(Double(element) * pow(256,Double(index))) } // Xcode 8 beta 6 // .map { (index, element) in return Int(element) * Int(pow(Double(256),Double(index))) } // Swift 2 .reduce(0, +) // Xcode 8 beta 6 // .reduce(0, combine: +) // Swift 2 or Xcode 8 beta 1 let filename = bytes[(i+46)..<(i+46+filenamelength)].reduce("", { $0 + String(format: "%c", $1)}) // Xcode 8 beta 6 // let filename = bytes[(i+46)..<(i+46+filenamelength)].reduce("", combine: { $0 + String(format: "%c", $1)}) // Swift 2 or Xcode 8 Beta 1 if !found && filename.hasPrefix(xmlfilenameprefix) { // Central directory : 42 4 Relative offset of local file header offset = bytes[(i+42)..<(i+42+4)] .enumerated() // .enumerate() // Swift 2 .map { (index, element) in return Int(Double(element) * pow(256,Double(index))) } // Xcode 8 beta 6 // .map { (index, element) in return Int(element) * Int(pow(Double(256),Double(index))) } // Swift 2 .reduce(0, +) // Xcode 8 beta 6 // .reduce(0, combine: +) // Swift 2 or Xcode 8 beta 1 // Local file header : offset+26 2 File name length (n) xmlfilenamelength = bytes[(offset+26)..<(offset+26+2)] .enumerated() // .enumerate() // Swift 2 .map { (index, element) in return Int(Double(element) * pow(256,Double(index))) } // Xcode 8 beta 6 // .map { (index, element) in return Int(element) * Int(pow(Double(256),Double(index))) } // Swift 2 .reduce(0, +) // Xcode 8 beta 6 // .reduce(0, combine: +) // Swift 2 or Xcode 8 beta 1 xmlfilename = bytes[(i+46)..<(i+46+xmlfilenamelength)].reduce("", { $0 + String(format: "%c", $1)}) // Xcode 8 beta 6 // xmlfilename = bytes[(i+46)..<(i+46+xmlfilenamelength)].reduce("", combine: { $0 + String(format: "%c", $1)}) // Swift 2 or Xcode 8 beta 1 found = true } else { if found && nextoffset == 0 { nextoffset = bytes[(i+42)..<(i+42+4)] .enumerated() // .enumerate() // Swift 2 .map { (index, element) in return Int(Double(element) * pow(256,Double(index))) } // Xcode 8 beta 6 // .map { (index, element) in return Int(element) * Int(pow(Double(256),Double(index))) } // Swift 2 .reduce(0, +) // Xcode 8 beta 6 // .reduce(0, combine: +) // Swift 2 or Xcode 8 beta 1 break } } } } //print(xmlfilename) // Local file header : offset+30 n File name let bytes2 = Array(bytes[(offset+30+xmlfilenamelength)..<(nextoffset > 0 ? nextoffset :firstcentralheader)]) if bytes2.count > 0 { dst = [UInt8](repeating: 0, count: bytes2.count*10) // destination buffer // dst = [UInt8](count: bytes2.count*10, repeatedValue: 0 ) // destination buffer // Swift 2 // use compression library function (iOS9 or macOS 10.11 or above) if #available(OSX 10.11, iOS 9.0, *) { let size = compression_decode_buffer(&dst, dst.count, bytes2, bytes2.count, nil, COMPRESSION_ZLIB) if size > 0 { if let outxml = String(bytes: dst[0..<size], encoding: String.Encoding.ascii) { // if let outxml = String(bytes: dst[0..<size], encoding: NSASCIIStringEncoding) { // Swift 2 return outxml } } } } return nil } timetest("Get MarKit xml", block:{ for date in ["20160808","20160809"] { for ccy in ["USD","GBP"] { if let xml = getMarkit(ccy: ccy, date: date) { print(xml) // printcurve(xml) // if using the InterestRateCurve() XMLParser to print curve points } } } })




No comments: