diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..62b0285 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yahoo Japan Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..89f1754 --- /dev/null +++ b/README.md @@ -0,0 +1,183 @@ +# SwiftyXMLParser +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ![Cocoapods compatible](https://cocoapod-badges.herokuapp.com/v/SwiftyXMLParser/badge.png) + + +Simple XML Parser implemented by Swift + +# What's this? +This is a XML parser inspired by [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) and [SWXMLHash](https://github.com/drmohundro/SWXMLHash). + +[NSXMLParser](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSXMLParser_Class/) in Foundation framework is a kind of "SAX" parser. It has a enough performance but is a little inconvenient. So we have implemented "DOM" parser wrapping it. + +# Feature +- [x] access XML Document with "subscript" literal. +- [x] access XML Document as SequenceType. +- [x] debug wrong XML pathes easily. + +# Requirement ++ iOS 7.0+ ++ Swift 2.0+ + +# Installation + +### Carthage +#### 1. create Cartfile + +```ruby:Cartfile +github "https://github.com/yahoojapan/SwiftyXMLParser" + +``` +#### 2. install +``` +> carthage update +``` + +### CocoaPods +#### 1. create Podfile +```ruby:Podfile +platform :ios, '8.0' +use_frameworks! + +pod "SwiftyXMLParser", :git => 'https://github.com/yahoojapan/SwiftyXMLParser.git' +``` + +#### 2. install +``` +> pod install +``` + +# Example + +```swift + let string = "Item1Item2" + + // parse xml document + xml = try! XML.parse(string) + + // access xml element + let accessor = xml["ResultSet"] + + // access XML Text + let text = xml["ResultSet", "Result", "Hit", 0, "Name"].text { + print("exsists path & text in XML Element") + } + + // access XML Attribute + let index = xml["ResultSet", "Result", "Hit"].attributes?["index"] { + print("exsists path & an attribute in XML Element") + } + + // enumerate child Elements in the parent Element + for hit in xml["ResultSet", "Result", "Hit"] { + print("enumarate existing XML Elements") + } + + // check if the XML path is wrong + if case .Failure(let error) = xml["ResultSet", "Result", "TypoKey"] { + print(error) + } +``` + +# Usage +### 1. Parse XML ++ from String +```swift +let string = "Item1Item2" + +xml = try! XML.parse(string) // -> XML.Accessor +``` ++ from NSData +```swift +let string = "Item1Item2" +let data = string.dataUsingEncoding(NSUTF8StringEncoding) + +xml = try! XML.parse(data) // -> XML.Accessor +``` + +### 2. Access child Elements +```swift +let element = xml["ResultSet"] // -> XML.Accessor +``` + +### 3. Access grandchild Elements ++ with String +```swift +let element = xml["ResultSet"]["Result"] // -> Item1Item2 +``` ++ with Array +```swift +let path = ["ResultSet", "Result"] +let element = xml[path] // -> Item1Item2 +``` ++ with Variadic +```swift +let element = xml["ResultSet", "Result"] // -> Item1Item2 +``` +### 4. Access specific grandchild Element +```swift +let element = xml["ResultSet", "Result", "Hit", 1] // -> Item2 +``` +### 5. Access attribute in Element +```swift +if let attributeValue = xml["ResultSet", "Result", "Hit", 1].attributes?["index"] { + print(attributeValue) // -> 2 +} +``` +### 6. Access text in Element ++ with optional binding +```swift +if let text = xml["ResultSet", "Result", "Hit", 1, "Name"].text { + print(text) // -> Item2 +} +``` ++ with custom operation +```swift +struct Entity { + var name = "" +} +let entity = Entity() +entity.name ?= xml["ResultSet", "Result", "Hit", 1, "Name"].text // assign if it has text +``` ++ convert Int and assign +```swift +struct Entity { + var name: Int = 0 +} +let entity = Entity() +entity.name ?= xml["ResultSet", "Result", "Hit", 1, "Name"].int // assign if it has Int +``` +and there are other syntax sugers, bool, url and double. ++ assign text into Array +```swift +struct Entity { + var names = [String]() +} +let entity = Entity() +entity.names ?<< xml["ResultSet", "Result", "Hit", 1, "Name"].text // assign if it has text +``` +### Check error +```swift +print(xml["ResultSet", "Result", "TypoKey"]) // -> "TypoKey not found." +``` + +### Access as SequenceType ++ for-in +```swift +for element in xml["ResultSet", "Result", "Hit"] { + print($0.text) +} +``` ++ map +```swift +xml["ResultSet", "Result", "Hit"].map { $0["Name"].text } +``` ++ filter +```swift +xml["ResultSet", "Result", "Hit"].filter { $0["Name"].text != "Item2" } +``` + +etc... + +# License + +This software is released under the MIT License, see LICENSE. diff --git a/SwiftyXMLParser.podspec b/SwiftyXMLParser.podspec new file mode 100755 index 0000000..845c30b --- /dev/null +++ b/SwiftyXMLParser.podspec @@ -0,0 +1,22 @@ +Pod::Spec.new do |s| + s.name = "SwiftyXMLParser" + s.version = "1.0.0" + s.summary = "Simple XML Parser implemented by Swift" + + s.description = <<-DESC + This is a XML parser inspired by SwiftyJSON and SWXMLHash. + + NSXMLParser in Foundation framework is a kind of "SAX" parser. It has a enough performance but is a little inconvenient. + So we have implemented "DOM" parser wrapping it. + DESC + + s.homepage = "https://github.com/yahoojapan/SwiftyXMLParser.git" + s.license = "MIT" + s.author = { "kahayash" => "kahayash@yahoo-corp.jp" } + + s.ios.deployment_target = "8.0" + s.source_files = "SwiftyXMLParser/*.swift" + s.requires_arc = true + + s.source = { :git => "https://github.com/yahoojapan/SwiftyXMLParser.git", :tag => "1.0.0" } +end diff --git a/SwiftyXMLParser.xcodeproj/project.pbxproj b/SwiftyXMLParser.xcodeproj/project.pbxproj new file mode 100755 index 0000000..cd620b3 --- /dev/null +++ b/SwiftyXMLParser.xcodeproj/project.pbxproj @@ -0,0 +1,452 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 2B225AAF1CBCBA40002282BD /* SimpleDocument.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2B225AAE1CBCBA40002282BD /* SimpleDocument.xml */; }; + 2B6D92371C7F0587000D2D06 /* SwiftyXMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B6D92361C7F0587000D2D06 /* SwiftyXMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2B6D923E1C7F0587000D2D06 /* SwiftyXMLParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B6D92331C7F0587000D2D06 /* SwiftyXMLParser.framework */; }; + 2B6D92431C7F0587000D2D06 /* SwiftyXMLParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B6D92421C7F0587000D2D06 /* SwiftyXMLParserTests.swift */; }; + 2B6D92511C7F059B000D2D06 /* XML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B6D924D1C7F059B000D2D06 /* XML.swift */; }; + 2B6D92521C7F059B000D2D06 /* Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B6D924E1C7F059B000D2D06 /* Element.swift */; }; + 2B6D92531C7F059B000D2D06 /* Accessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B6D924F1C7F059B000D2D06 /* Accessor.swift */; }; + 2B6D92541C7F059B000D2D06 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B6D92501C7F059B000D2D06 /* Parser.swift */; }; + 2BD9432B1CB220E0007D5FFC /* XMLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD9432A1CB220E0007D5FFC /* XMLTests.swift */; }; + 2BD9432D1CB220E5007D5FFC /* ParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD9432C1CB220E5007D5FFC /* ParserTests.swift */; }; + 2BD9432F1CB220EA007D5FFC /* AccessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD9432E1CB220EA007D5FFC /* AccessorTests.swift */; }; + 2BD943311CB220F1007D5FFC /* CustomOperatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD943301CB220F1007D5FFC /* CustomOperatorTests.swift */; }; + 2BD943341CB22107007D5FFC /* BrokenXMLDocument.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2BD943331CB22107007D5FFC /* BrokenXMLDocument.xml */; }; + 2BD943361CB2210D007D5FFC /* XMLDocument.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2BD943351CB2210D007D5FFC /* XMLDocument.xml */; }; + 2BD943381CB23CFE007D5FFC /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD943371CB23CFE007D5FFC /* Error.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 2B6D923F1C7F0587000D2D06 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2B6D922A1C7F0587000D2D06 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2B6D92321C7F0587000D2D06; + remoteInfo = SwiftyXMLParser; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 2B225AAE1CBCBA40002282BD /* SimpleDocument.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = SimpleDocument.xml; sourceTree = ""; }; + 2B6D92331C7F0587000D2D06 /* SwiftyXMLParser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyXMLParser.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2B6D92361C7F0587000D2D06 /* SwiftyXMLParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftyXMLParser.h; sourceTree = ""; }; + 2B6D92381C7F0587000D2D06 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2B6D923D1C7F0587000D2D06 /* SwiftyXMLParserTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftyXMLParserTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 2B6D92421C7F0587000D2D06 /* SwiftyXMLParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftyXMLParserTests.swift; sourceTree = ""; }; + 2B6D92441C7F0587000D2D06 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2B6D924D1C7F059B000D2D06 /* XML.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XML.swift; sourceTree = ""; }; + 2B6D924E1C7F059B000D2D06 /* Element.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Element.swift; sourceTree = ""; }; + 2B6D924F1C7F059B000D2D06 /* Accessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Accessor.swift; sourceTree = ""; }; + 2B6D92501C7F059B000D2D06 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = ""; }; + 2BD9432A1CB220E0007D5FFC /* XMLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLTests.swift; sourceTree = ""; }; + 2BD9432C1CB220E5007D5FFC /* ParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParserTests.swift; sourceTree = ""; }; + 2BD9432E1CB220EA007D5FFC /* AccessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessorTests.swift; sourceTree = ""; }; + 2BD943301CB220F1007D5FFC /* CustomOperatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomOperatorTests.swift; sourceTree = ""; }; + 2BD943331CB22107007D5FFC /* BrokenXMLDocument.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = BrokenXMLDocument.xml; sourceTree = ""; }; + 2BD943351CB2210D007D5FFC /* XMLDocument.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = XMLDocument.xml; sourceTree = ""; }; + 2BD943371CB23CFE007D5FFC /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2B6D922F1C7F0587000D2D06 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2B6D923A1C7F0587000D2D06 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2B6D923E1C7F0587000D2D06 /* SwiftyXMLParser.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2B6D92291C7F0587000D2D06 = { + isa = PBXGroup; + children = ( + 2B6D92351C7F0587000D2D06 /* SwiftyXMLParser */, + 2B6D92411C7F0587000D2D06 /* SwiftyXMLParserTests */, + 2B6D92341C7F0587000D2D06 /* Products */, + ); + sourceTree = ""; + }; + 2B6D92341C7F0587000D2D06 /* Products */ = { + isa = PBXGroup; + children = ( + 2B6D92331C7F0587000D2D06 /* SwiftyXMLParser.framework */, + 2B6D923D1C7F0587000D2D06 /* SwiftyXMLParserTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 2B6D92351C7F0587000D2D06 /* SwiftyXMLParser */ = { + isa = PBXGroup; + children = ( + 2B6D92361C7F0587000D2D06 /* SwiftyXMLParser.h */, + 2B6D924D1C7F059B000D2D06 /* XML.swift */, + 2B6D924E1C7F059B000D2D06 /* Element.swift */, + 2B6D924F1C7F059B000D2D06 /* Accessor.swift */, + 2B6D92501C7F059B000D2D06 /* Parser.swift */, + 2BD943371CB23CFE007D5FFC /* Error.swift */, + 2B6D92381C7F0587000D2D06 /* Info.plist */, + ); + path = SwiftyXMLParser; + sourceTree = ""; + }; + 2B6D92411C7F0587000D2D06 /* SwiftyXMLParserTests */ = { + isa = PBXGroup; + children = ( + 2BD943321CB220FA007D5FFC /* Fixture */, + 2B6D92421C7F0587000D2D06 /* SwiftyXMLParserTests.swift */, + 2BD9432A1CB220E0007D5FFC /* XMLTests.swift */, + 2BD9432C1CB220E5007D5FFC /* ParserTests.swift */, + 2BD9432E1CB220EA007D5FFC /* AccessorTests.swift */, + 2BD943301CB220F1007D5FFC /* CustomOperatorTests.swift */, + 2B6D92441C7F0587000D2D06 /* Info.plist */, + ); + path = SwiftyXMLParserTests; + sourceTree = ""; + }; + 2BD943321CB220FA007D5FFC /* Fixture */ = { + isa = PBXGroup; + children = ( + 2BD943351CB2210D007D5FFC /* XMLDocument.xml */, + 2BD943331CB22107007D5FFC /* BrokenXMLDocument.xml */, + 2B225AAE1CBCBA40002282BD /* SimpleDocument.xml */, + ); + name = Fixture; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2B6D92301C7F0587000D2D06 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2B6D92371C7F0587000D2D06 /* SwiftyXMLParser.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 2B6D92321C7F0587000D2D06 /* SwiftyXMLParser */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2B6D92471C7F0587000D2D06 /* Build configuration list for PBXNativeTarget "SwiftyXMLParser" */; + buildPhases = ( + 2B6D922E1C7F0587000D2D06 /* Sources */, + 2B6D922F1C7F0587000D2D06 /* Frameworks */, + 2B6D92301C7F0587000D2D06 /* Headers */, + 2B6D92311C7F0587000D2D06 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftyXMLParser; + productName = SwiftyXMLParser; + productReference = 2B6D92331C7F0587000D2D06 /* SwiftyXMLParser.framework */; + productType = "com.apple.product-type.framework"; + }; + 2B6D923C1C7F0587000D2D06 /* SwiftyXMLParserTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2B6D924A1C7F0587000D2D06 /* Build configuration list for PBXNativeTarget "SwiftyXMLParserTests" */; + buildPhases = ( + 2B6D92391C7F0587000D2D06 /* Sources */, + 2B6D923A1C7F0587000D2D06 /* Frameworks */, + 2B6D923B1C7F0587000D2D06 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2B6D92401C7F0587000D2D06 /* PBXTargetDependency */, + ); + name = SwiftyXMLParserTests; + productName = SwiftyXMLParserTests; + productReference = 2B6D923D1C7F0587000D2D06 /* SwiftyXMLParserTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 2B6D922A1C7F0587000D2D06 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = "Yahoo! Japan"; + TargetAttributes = { + 2B6D92321C7F0587000D2D06 = { + CreatedOnToolsVersion = 7.2.1; + }; + 2B6D923C1C7F0587000D2D06 = { + CreatedOnToolsVersion = 7.2.1; + }; + }; + }; + buildConfigurationList = 2B6D922D1C7F0587000D2D06 /* Build configuration list for PBXProject "SwiftyXMLParser" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 2B6D92291C7F0587000D2D06; + productRefGroup = 2B6D92341C7F0587000D2D06 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2B6D92321C7F0587000D2D06 /* SwiftyXMLParser */, + 2B6D923C1C7F0587000D2D06 /* SwiftyXMLParserTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2B6D92311C7F0587000D2D06 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2B6D923B1C7F0587000D2D06 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2BD943361CB2210D007D5FFC /* XMLDocument.xml in Resources */, + 2B225AAF1CBCBA40002282BD /* SimpleDocument.xml in Resources */, + 2BD943341CB22107007D5FFC /* BrokenXMLDocument.xml in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2B6D922E1C7F0587000D2D06 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2B6D92541C7F059B000D2D06 /* Parser.swift in Sources */, + 2B6D92521C7F059B000D2D06 /* Element.swift in Sources */, + 2B6D92511C7F059B000D2D06 /* XML.swift in Sources */, + 2BD943381CB23CFE007D5FFC /* Error.swift in Sources */, + 2B6D92531C7F059B000D2D06 /* Accessor.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2B6D92391C7F0587000D2D06 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2BD943311CB220F1007D5FFC /* CustomOperatorTests.swift in Sources */, + 2BD9432F1CB220EA007D5FFC /* AccessorTests.swift in Sources */, + 2B6D92431C7F0587000D2D06 /* SwiftyXMLParserTests.swift in Sources */, + 2BD9432B1CB220E0007D5FFC /* XMLTests.swift in Sources */, + 2BD9432D1CB220E5007D5FFC /* ParserTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 2B6D92401C7F0587000D2D06 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2B6D92321C7F0587000D2D06 /* SwiftyXMLParser */; + targetProxy = 2B6D923F1C7F0587000D2D06 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 2B6D92451C7F0587000D2D06 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2B6D92461C7F0587000D2D06 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 2B6D92481C7F0587000D2D06 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = SwiftyXMLParser/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.yahoo.shopping.SwiftyXMLParser; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 2B6D92491C7F0587000D2D06 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = SwiftyXMLParser/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.yahoo.shopping.SwiftyXMLParser; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 2B6D924B1C7F0587000D2D06 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = SwiftyXMLParserTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.yahoo.shopping.SwiftyXMLParserTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 2B6D924C1C7F0587000D2D06 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = SwiftyXMLParserTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.yahoo.shopping.SwiftyXMLParserTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2B6D922D1C7F0587000D2D06 /* Build configuration list for PBXProject "SwiftyXMLParser" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2B6D92451C7F0587000D2D06 /* Debug */, + 2B6D92461C7F0587000D2D06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2B6D92471C7F0587000D2D06 /* Build configuration list for PBXNativeTarget "SwiftyXMLParser" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2B6D92481C7F0587000D2D06 /* Debug */, + 2B6D92491C7F0587000D2D06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2B6D924A1C7F0587000D2D06 /* Build configuration list for PBXNativeTarget "SwiftyXMLParserTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2B6D924B1C7F0587000D2D06 /* Debug */, + 2B6D924C1C7F0587000D2D06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 2B6D922A1C7F0587000D2D06 /* Project object */; +} diff --git a/SwiftyXMLParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SwiftyXMLParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100755 index 0000000..6757702 --- /dev/null +++ b/SwiftyXMLParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftyXMLParser.xcodeproj/project.xcworkspace/xcuserdata/kahayash.xcuserdatad/UserInterfaceState.xcuserstate b/SwiftyXMLParser.xcodeproj/project.xcworkspace/xcuserdata/kahayash.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..569280f Binary files /dev/null and b/SwiftyXMLParser.xcodeproj/project.xcworkspace/xcuserdata/kahayash.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SwiftyXMLParser.xcodeproj/xcshareddata/xcschemes/SwiftyXMLParser.xcscheme b/SwiftyXMLParser.xcodeproj/xcshareddata/xcschemes/SwiftyXMLParser.xcscheme new file mode 100755 index 0000000..51da0db --- /dev/null +++ b/SwiftyXMLParser.xcodeproj/xcshareddata/xcschemes/SwiftyXMLParser.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftyXMLParser.xcodeproj/xcuserdata/kahayash.xcuserdatad/xcschemes/xcschememanagement.plist b/SwiftyXMLParser.xcodeproj/xcuserdata/kahayash.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100755 index 0000000..fe3cda3 --- /dev/null +++ b/SwiftyXMLParser.xcodeproj/xcuserdata/kahayash.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,27 @@ + + + + + SchemeUserState + + SwiftyXMLParser.xcscheme_^#shared#^_ + + orderHint + 0 + + + SuppressBuildableAutocreation + + 2B6D92321C7F0587000D2D06 + + primary + + + 2B6D923C1C7F0587000D2D06 + + primary + + + + + diff --git a/SwiftyXMLParser/Accessor.swift b/SwiftyXMLParser/Accessor.swift new file mode 100755 index 0000000..facca11 --- /dev/null +++ b/SwiftyXMLParser/Accessor.swift @@ -0,0 +1,442 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import Foundation + +extension XML { + + /** + Class for accessing XML Document as SequenceType. + + defined as enumeration which has three states + - SingleElement + - accesssed the specific XML Element from XML Path + - Sequence + - accessed multiple Elements from XML Path + - Failure + - wrong XML Path + + + */ + public enum Accessor: CustomStringConvertible, SequenceType { + case SingleElement(Element) + case Sequence([Element]) + case Failure(Error) + + public init(_ element: Element) { + self = .SingleElement(element) + } + + public init(_ sequence: [Element]) { + self = .Sequence(sequence) + } + + public init(_ error: Error) { + self = .Failure(error) + } + + /** + If Accessor object has a correct XML path, return XML element, otherwith return error + + example: + + ``` + let hit = xml[1][2][3] + ``` + + + The same as: + ``` + let hit = xml[1, 2, 3] + ``` + + or : + ``` + let path = [1, 2, 3] + let hit = xml[path] + ``` + + */ + private subscript(index index: Int) -> Accessor { + let accessor: Accessor + switch self { + case .Sequence(let elements) where index < elements.count: + accessor = Accessor(elements[index]) + case .SingleElement(let element) where index == 0: + accessor = Accessor(element) + case .Failure(let error): + accessor = Accessor(error) + break + default: + let error = accessError("cannot access Index: \(index)") + accessor = Accessor(error) + break + } + return accessor + } + + /** + If Accessor object has a correct XML path, return XML element, otherwith return error + + example: + + ``` + let hit = xml["ResultSet"]["Result"]["Hit"] + ``` + + + The same as: + ``` + let hit = xml["ResultSet", "Result", "Hit"] + ``` + + or : + ``` + let path = ["ResultSet", "Result", "Hit"] + let hit = xml[path] + ``` + + */ + private subscript(key key: String) -> Accessor { + let accessor: Accessor + switch self { + case .SingleElement(let element): + let filterdElements = element.childElements.filter { $0.name == key } + if filterdElements.isEmpty { + let error = accessError("\(key) not found.") + accessor = Accessor(error) + } else if filterdElements.count == 1 { + accessor = Accessor(filterdElements[0]) + } else { + accessor = Accessor(filterdElements) + } + case .Failure(let error): + accessor = Accessor(error) + case .Sequence(_): + fallthrough + default: + let error = accessError("cannot access \(key), because of multiple elements") + accessor = Accessor(error) + break + } + return accessor + } + + /** + If Accessor object has a correct XML path, return XML element, otherwith return error + + example: + ``` + let path = ["ResultSet", "Result", "Hit", 0] + let hit = xml[path] + ``` + + The same as: + ``` + let hit = xml["ResultSet", "Result", "Hit", 0] + ``` + + or : + + ``` + let hit = xml["ResultSet"]["Result"]["Hit"][0] + ``` + */ + public subscript(path: Array) -> Accessor { + var accessor = self + for position in path { + switch position { + case let index as Int: + accessor = accessor[index] + case let key as String: + accessor = accessor[key] + default: + let error = accessError("cannot access \(position)") + accessor = Accessor(error) + } + } + return accessor + } + + /** + If Accessor object has a correct XML path, return XML element, otherwith return error + + example: + ``` + let hit = xml["ResultSet", "Result", "Hit", 0] + ``` + + + The same as: + + ``` + let path = ["ResultSet", "Result", "Hit", 0] + let hit = xml[path] + ``` + or : + + ``` + let hit = xml["ResultSet"]["Result"]["Hit"][0] + ``` + */ + public subscript(path: XMLSubscriptType...) -> Accessor { + var accessor = self + for position in path { + switch position { + case let index as Int: + accessor = accessor[index: index] + case let key as String: + accessor = accessor[key: key] + default: + let error = accessError("cannot access \(position)") + accessor = Accessor(error) + } + } + return accessor + } + + public var name: String? { + let name: String? + switch self { + case .SingleElement(let element): + name = element.name + case .Failure(_), .Sequence(_): + fallthrough + default: + name = nil + break + } + return name + } + + public var text: String? { + let text: String? + switch self { + case .SingleElement(let element): + text = element.text + case .Failure(_), .Sequence(_): + fallthrough + default: + text = nil + break + } + return text + } + + + /// syntax sugar to access Bool Text + public var bool: Bool? { + return text.flatMap { $0 == "true" } + } + + + /// syntax sugar to access URL Text + public var url: NSURL? { + return text.flatMap({NSURL(string: $0)}) + } + + /// syntax sugar to access Int Text + public var int: Int? { + return text.flatMap({Int($0)}) + } + + /// syntax sugar to access Double Text + public var double: Double? { + return text.flatMap({Double($0)}) + } + + /// access to XML Attributes + public var attributes: [String: String] { + let attributes: [String: String] + switch self { + case .SingleElement(let element): + attributes = element.attributes + case .Failure(_), .Sequence(_): + fallthrough + default: + attributes = [String: String]() + break + } + return attributes + } + + /// access to child Elements + public var all: [Element]? { + switch self { + case .SingleElement(let element): + return [element] + case .Sequence(let elements): + return elements + case .Failure(_): + return nil + } + } + + /// access to child Elemnet Tag Names + public var names: [String]? { + switch self { + case .SingleElement(let element): + return [element.name] + case .Sequence(let elements): + return elements.map { $0.name } + case .Failure(_): + return nil + } + } + + /// if it has wrong XML path, return Error, otherwise return nil + public var error: Error? { + switch self { + case .Failure(let error): + return error + case .SingleElement(_), .Sequence(_): + return nil + } + } + + + /// if it has wrong XML path or multiple child elements, return nil, otherwise return Element + public var element: Element? { + switch self { + case .SingleElement(let element): + return element + case .Failure(_), .Sequence(_): + return nil + } + } + + /// if it has wrong XML path or no child Element, return nil, otherwise return last Element + public var last: Accessor { + switch self { + case .SingleElement(let element): + return Accessor(element) + case .Sequence(let elements): + if let lastElement = elements.last { + return Accessor(lastElement) + } else { + return Accessor(accessError("cannot access last element")) + } + case .Failure(let error): + return Accessor(error) + } + } + + /// if it has wrong XML path or no child Element, return nil, otherwise return first Element + public var first: Accessor { + switch self { + case .SingleElement(let element): + return Accessor(element) + case .Sequence(let elements): + if let firstElement = elements.first { + return Accessor(firstElement) + } else { + return Accessor(accessError("cannot access first element")) + } + case .Failure(let error): + return Accessor(error) + } + } + + public func map(transform: (Accessor) -> T) -> [T] { + switch self { + case .SingleElement(let element): + return [Accessor(element)].map(transform) + case .Sequence(let elements): + return elements.map({ Accessor($0) }).map(transform) + case .Failure: + return [Accessor]().map(transform) + } + } + + @available(*, renamed="flatMap") + public func mapWithSqueezeNil(transform: (Accessor) -> T?) -> [T] { + var accessors = [Accessor]() + switch self { + case .SingleElement(let element): + accessors = [Accessor(element)] + case .Sequence(let elements): + accessors = elements.map({ Accessor($0) }) + case .Failure: + accessors = [Accessor]() + } + return accessors.reduce([T]()) { + let transformed = transform($1) + if let unwrappedTransform = transformed { + return $0 + [unwrappedTransform] + } else { + return $0 + } + } + } + + // MARK :- SequenceType + + public func generate() -> AnyGenerator { + let generator: [Element] + switch self { + case .Failure(_): + generator = [Element]() + case .SingleElement(let element): + generator = [element] + case .Sequence(let elements): + generator = elements + } + var index = 0 + return anyGenerator { + if index < generator.count { + return Accessor(generator[index++]) + } else { + return nil + } + } + } + + + // MARK: - CustomStringConvertible + + public var description: String { + switch self { + case .SingleElement(let element): + return "\"" + self.recursivePrintAncient(element) + "\"" + case .Sequence(let elements): + let descriptions = elements.map { self.recursivePrintAncient($0) } + return "[ " + descriptions.joinWithSeparator(",\n ") + " ]" + case .Failure(let error): + return "\(error)" + } + } + + private func recursivePrintAncient(element: Element) -> String { + var description = element.name + if let unwrappedParent = element.parentElement { + description = recursivePrintAncient(unwrappedParent) + " > " + description + } + return description + } + + private func accessError(description: String) -> Error { + return Error.AccessError(description: description) + } + } +} diff --git a/SwiftyXMLParser/Element.swift b/SwiftyXMLParser/Element.swift new file mode 100755 index 0000000..fc62498 --- /dev/null +++ b/SwiftyXMLParser/Element.swift @@ -0,0 +1,41 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import Foundation + +extension XML { + public class Element { + public var name: String + public var text: String? + public var attributes = [String: String]() + public var childElements = [Element]() + + // for println + public weak var parentElement: Element? + + public init(name: String) { + self.name = name + } + } +} \ No newline at end of file diff --git a/SwiftyXMLParser/Error.swift b/SwiftyXMLParser/Error.swift new file mode 100755 index 0000000..5c05d8a --- /dev/null +++ b/SwiftyXMLParser/Error.swift @@ -0,0 +1,32 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import Foundation + +extension XML { + public enum Error: ErrorType { + case ParseError + case AccessError(description: String) + } +} \ No newline at end of file diff --git a/SwiftyXMLParser/Info.plist b/SwiftyXMLParser/Info.plist new file mode 100755 index 0000000..d3de8ee --- /dev/null +++ b/SwiftyXMLParser/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/SwiftyXMLParser/Parser.swift b/SwiftyXMLParser/Parser.swift new file mode 100755 index 0000000..8211d65 --- /dev/null +++ b/SwiftyXMLParser/Parser.swift @@ -0,0 +1,79 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import Foundation + +extension XML { + class Parser: NSObject, NSXMLParserDelegate { + func parse(data: NSData) -> Accessor { + stack = [Element]() + stack.append(documentRoot) + let parser = NSXMLParser(data: data) + parser.delegate = self + parser.parse() + return Accessor(documentRoot) + } + + override init() { + trimmingManner = nil + } + + init(trimming manner: NSCharacterSet) { + trimmingManner = manner + } + + // MARK:- private + private var documentRoot = Element(name: "XML.Parser.AbstructedDocumentRoot") + private var stack = [Element]() + private let trimmingManner: NSCharacterSet? + + func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { + let node = Element(name: elementName) + if !attributeDict.isEmpty { + node.attributes = attributeDict + } + + let parentNode = stack.last + + node.parentElement = parentNode + parentNode?.childElements.append(node) + stack.append(node) + } + + func parser(parser: NSXMLParser, foundCharacters string: String) { + if let text = stack.last?.text { + stack.last?.text = text + (string ?? "") + } else { + stack.last?.text = "" + (string ?? "") + } + } + + func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { + if let trimmingManner = self.trimmingManner { + stack.last?.text = stack.last?.text?.stringByTrimmingCharactersInSet(trimmingManner) + } + stack.removeLast() + } + } +} \ No newline at end of file diff --git a/SwiftyXMLParser/SwiftyXMLParser.h b/SwiftyXMLParser/SwiftyXMLParser.h new file mode 100755 index 0000000..b2f05f0 --- /dev/null +++ b/SwiftyXMLParser/SwiftyXMLParser.h @@ -0,0 +1,35 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import + +//! Project version number for SwiftyXMLParser. +FOUNDATION_EXPORT double SwiftyXMLParserVersionNumber; + +//! Project version string for SwiftyXMLParser. +FOUNDATION_EXPORT const unsigned char SwiftyXMLParserVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/SwiftyXMLParser/XML.swift b/SwiftyXMLParser/XML.swift new file mode 100755 index 0000000..738069e --- /dev/null +++ b/SwiftyXMLParser/XML.swift @@ -0,0 +1,138 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import Foundation + +/// available Type in XML.Accessor subscript +public protocol XMLSubscriptType {} +extension Int: XMLSubscriptType {} +extension String: XMLSubscriptType {} + +infix operator ?= {} // Failable Assignment + +/** + assign value if rhs is not optonal. When rhs is optional, nothing to do. +*/ +public func ?=(inout lhs: T, rhs: T?) { + if let unwrappedRhs = rhs { + lhs = unwrappedRhs + } +} + +infix operator ?<< {} // Failable Push + +/** + push value to array if rhs is not optonal. When rhs is optional, nothing to do. +*/ +public func ?<< (inout lhs: [T], rhs: T?) { + if let unwrappedRhs = rhs { + lhs.append(unwrappedRhs) + } +} + + +/** + Director class to parse and access XML document. + + You can parse XML docuemnts with parse() method and get the accessor. + + ### Example + ``` + let string = "ほげふが" + xml = XML.parse(string) + let text = xml["ResultSet"]["Result"]["Hit"][0]["Name"].text { + println("exsists path & text") + } + + let text = xml["ResultSet", "Result", "Hit", 0, "Name"].text { + println("exsists path & text") + } + + let attributes = xml["ResultSet", "Result", "Hit", 0, "Name"].attributes { + println("exsists path & attributes") + } + + for hit in xml["ResultSet", "Result", "Hit"] { + println("enumarate existing element") + } + + switch xml["ResultSet", "Result", "TypoKey"] { + case .Failure(let error): + println(error) + case .SingleElement(_), .Sequence(_): + println("success parse") + } + ``` +*/ +public class XML { + /** + Interface to parse NSData + + - parameter data:NSData XML document + - returns:Accessor object to access XML document + */ + public class func parse(data: NSData) -> Accessor { + return Parser().parse(data) + } + + /** + Interface to parse String + + - Parameter str:String XML document + - Returns:Accessor object to access XML document + */ + public class func parse(str: String) throws -> Accessor { + guard let data = str.dataUsingEncoding(NSUTF8StringEncoding) else { + throw XML.Error.ParseError + } + + return Parser().parse(data) + } + + /** + Interface to parse NSData + + - parameter data:NSData XML document + - parameter manner:NSCharacterSet If you wannna trim Text, assign this arg + - returns:Accessor object to access XML document + */ + public class func parse(data: NSData, trimming manner: NSCharacterSet) -> Accessor { + return Parser(trimming: manner).parse(data) + } + + /** + Interface to parse String + + - Parameter str:String XML document + - parameter manner:NSCharacterSet If you wannna trim Text, assign this arg + - Returns:Accessor object to access XML document + */ + public class func parse(str: String, trimming manner: NSCharacterSet) throws -> Accessor { + guard let data = str.dataUsingEncoding(NSUTF8StringEncoding) else { + throw XML.Error.ParseError + } + + return Parser(trimming: manner).parse(data) + } +} \ No newline at end of file diff --git a/SwiftyXMLParserTests/AccessorTests.swift b/SwiftyXMLParserTests/AccessorTests.swift new file mode 100755 index 0000000..53253d0 --- /dev/null +++ b/SwiftyXMLParserTests/AccessorTests.swift @@ -0,0 +1,404 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import UIKit +import XCTest +@testable import SwiftyXMLParser + +class AccessorTests: XCTestCase { + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testIndexerTypeIntToSingleElement() { + let singleElementAccessor = XML.Accessor(singleElement()) + let target1 = singleElementAccessor[0] + if let name = target1.name { + XCTAssertEqual(name, "RootElement", "can access element name") + } else { + XCTFail("fail to get name") + } + + let target2 = singleElementAccessor[1] + switch target2 { + case .Failure(_): + XCTAssert(true, "access to wrong path") + default: + XCTFail("need to fail") + } + } + + func testIndexerTypeIntToSequence() { + let accessor = XML.Accessor(sequence()) + let target1 = accessor[0] + if let name = target1.name { + XCTAssertEqual(name, "Element", "can access corrent element") + } else { + XCTFail("fail to get name") + } + + let target2 = accessor[1] + if let name = target2.name { + XCTAssertEqual(name, "Element", "can access corrent element") + } else { + XCTFail("fail to get name") + } + + let target3 = accessor[2] + switch target3 { + case .Failure(_): + XCTAssert(true, "Aaccess to wrong path") + default: + XCTFail("need to fail") + } + } + + func testIndexerTypeString() { + let accessor = XML.Accessor(singleElement()) + let me = accessor["RootElement"] + switch me { + case .Failure(_): + XCTAssert(true, "can access corrent element") + default: + XCTFail("fail to get element") + } + } + + func testIndexerTypeStringShingleElement() { + let accessor = XML.Accessor(singleElement()) + let children = accessor["ChildElement"] + switch children { + case .Sequence(_): + XCTAssert(true, "can access corrent element") + default: + XCTFail("fail to get element") + } + } + + func testIndexerTypeStringSequence() { + let accessor = XML.Accessor(sequence()) + let failureChildren = accessor["ChildElement"] + switch failureChildren { + case .Failure(_): + XCTAssert(true, "need to select one element from multiple elements") + default: + XCTFail("need to fail") + } + + let successChildren = accessor[0]["ChildElement1"] + switch successChildren { + case .Sequence(_): + XCTAssert(true, "can access corrent element") + default: + XCTFail("fail to get element") + } + } + + func testIndexerToFailure() { + let accessor = XML.Accessor(failure()) + let intIndexer = accessor[0] + switch intIndexer { + case .Failure(_): + XCTAssert(true, "need to return failure when access wrong path once") + default: + XCTFail("need to fail") + } + + let stringIndexer = accessor["ChildElement"] + switch stringIndexer { + case .Failure(_): + XCTAssert(true, "need to return failure when access wrong path once") + default: + XCTFail("need to fail") + } + } + + func testIndexTypeArray() { + let accessor = XML.Accessor(sequence()) + let indexer = accessor[[0, "ChildElement1", 1]] + switch indexer { + case .SingleElement(_): + XCTAssert(true, "access element with Array") + default: + XCTFail("fail to get element") + } + + let failureIndexer = accessor[[1, "Hoget", "Foge"]] + switch failureIndexer { + case .Failure(_): + XCTAssert(true, "access wrong path with Array") + default: + XCTFail("need to fail") + } + } + + func testIndexTypeVariableArguments() { + let accessor = XML.Accessor(sequence()) + let indexer = accessor[0, "ChildElement1", 1] + switch indexer { + case .SingleElement(_): + XCTAssert(true, "access element with Variadic") + default: + XCTFail("fail to get element") + } + + let failureIndexer = accessor[1, "Hoget", "Foge"] + switch failureIndexer { + case .Failure(_): + XCTAssert(true, "access wrong path with Variadic") + default: + XCTFail("need to fail") + } + } + + func testName() { + let accessor = XML.Accessor(singleElement()) + if let name = accessor.name { + XCTAssertEqual(name, "RootElement", "access name with SingleElement Accessor") + } else { + XCTFail("fail") + } + + let sequenceAccessor = XML.Accessor(sequence()) + if let _ = sequenceAccessor.name { + XCTFail("access name with Failure Sequence") + } else { + XCTAssert(true, "fail") + } + + let failureAccessor = XML.Accessor(failure()) + if let _ = failureAccessor.name { + XCTFail("fail") + } else { + XCTAssert(true, "fail to access name with Failure Accessor") + } + } + + func testText() { + let accessor = XML.Accessor(singleElement()) + if let text = accessor.text { + XCTAssertEqual(text, "text", "access text with SingleElement Accessor") + } else { + XCTFail("fail") + } + + let sequenceAccessor = XML.Accessor(sequence()) + if let _ = sequenceAccessor.text { + XCTFail("fail") + } else { + XCTAssert(true, "fail to access text with Sequence Accessor") + } + + let failureAccessor = XML.Accessor(failure()) + if let _ = failureAccessor.text { + XCTFail("fail") + } else { + XCTAssert(true, "fail to access name with Failure Accessor") + } + } + + func testAttributes() { + let accessor = XML.Accessor(singleElement()) + if !accessor.attributes.isEmpty { + XCTAssertEqual(accessor.attributes, ["key": "value"], "access attriubtes with SingleElement Accessor") + } else { + XCTFail("fail") + } + + let sequenceAccessor = XML.Accessor(sequence()) + if !sequenceAccessor.attributes.isEmpty { + XCTFail("fail") + } else { + XCTAssert(true, "fail to attributes text with Sequence Accessor") + } + + let failureAccessor = XML.Accessor(failure()) + if !failureAccessor.attributes.isEmpty { + XCTFail("fail") + } else { + XCTAssert(true, "fail to access name with Failure Accessor") + } + } + + func testAll() { + let accessor = XML.Accessor(singleElement()) + if let all = accessor.all { + XCTAssertEqual(all.count, 1, "access all elements") + } else { + XCTFail("fail") + } + + let sequenceAccessor = XML.Accessor(sequence()) + if let all = sequenceAccessor.all { + XCTAssertEqual(all.count, 2, "access all elements") + } else { + XCTAssert(true, "fail") + } + + let failureAccessor = XML.Accessor(failure()) + if let _ = failureAccessor.all { + XCTFail("access all elements") + } else { + XCTAssert(true, "fail") + } + } + + func testNames() { + let accessor = XML.Accessor(singleElement()) + if let names = accessor.names { + XCTAssertEqual(names[0], "RootElement", "access all names") + } else { + XCTFail("fail") + } + + let sequenceAccessor = XML.Accessor(sequence()) + if let names = sequenceAccessor.names { + XCTAssertEqual(names, ["Element", "Element"], "access all names") + } else { + XCTFail("fail") + } + + let failureAccessor = XML.Accessor(failure()) + if let _ = failureAccessor.names { + XCTFail("fail") + } else { + XCTAssert(true, "fail to access all names") + } + } + + func testError() { + let accessor = XML.Accessor(singleElement()) + if let _ = accessor.error { + XCTFail("fail") + } else { + XCTAssert(true, "return nil from SngleElement") + + } + + let sequenceAccessor = XML.Accessor(sequence()) + if let _ = sequenceAccessor.error { + XCTFail("fail") + } else { + XCTAssert(true, "return nil from SngleElement") + + } + + let failureAccessor = XML.Accessor(failure()) + if let _ = failureAccessor.error { + XCTAssert(true, "return Error from SngleElement") + } else { + XCTFail("fail") + } + } + + func testMap() { + let accessor = XML.Accessor(singleElement()) + let newAccessor = accessor.map { $0 } + XCTAssertEqual(newAccessor.count, 1, "access single element with map") + + let sequenceAccessor = XML.Accessor(sequence()) + let newSequenceAccessor = sequenceAccessor.map { $0 } + XCTAssertEqual(newSequenceAccessor.count, 2, "access each element with map") + + let failureAccessor = XML.Accessor(failure()) + let newFailureAccessor = failureAccessor.map { $0 } + XCTAssertEqual(newFailureAccessor.count, 0, "access failure with map") + } + + func testFlatMap() { + let accessor = XML.Accessor(singleElement()) + let singleText = accessor.flatMap { $0.text } + XCTAssertEqual(singleText, ["text"], "can access text") + + let sequenceAccessor = XML.Accessor(sequence()) + let texts = sequenceAccessor.flatMap { $0.text } + XCTAssertEqual(texts, ["text", "text2"], "can access each text") + + let failureAccessor = XML.Accessor(failure()) + let failureTexts = failureAccessor.flatMap { $0.text } + XCTAssertEqual(failureTexts, [], "has no text") + } + + + func testIterator() { + let accessor = XML.Accessor(singleElement()) + var result: [XML.Accessor] = [] + for accessorElem in accessor { + result.append(accessorElem) + } + XCTAssertEqual(result.count, 1, "access single element with for-in") + + let sequneceAccessor = XML.Accessor(sequence()) + var sequenceResult: [XML.Accessor] = [] + for accessorElem in sequneceAccessor { + sequenceResult.append(accessorElem) + } + XCTAssertEqual(sequenceResult.count, 2, "access multiple element with for-in") + + let failureAccessor = XML.Accessor(failure()) + var failureResult: [XML.Accessor] = [] + for accessorElem in failureAccessor { + failureResult.append(accessorElem) + } + XCTAssertEqual(failureResult.count, 0, "access failure element with for-in") + } + + private func singleElement() -> XML.Element { + let element = XML.Element(name: "RootElement") + element.text = "text" + element.attributes = ["key": "value"] + element.childElements = [ + XML.Element(name: "ChildElement"), + XML.Element(name: "ChildElement") + ] + return element + } + + private func sequence() -> [XML.Element] { + let elem1 = XML.Element(name: "Element") + elem1.text = "text" + elem1.attributes = ["key": "value"] + elem1.childElements = [ + XML.Element(name: "ChildElement1"), + XML.Element(name: "ChildElement1") + ] + let elem2 = XML.Element(name: "Element") + elem2.text = "text2" + elem2.childElements = [ + XML.Element(name: "ChildElement2"), + XML.Element(name: "ChildElement2") + ] + let elements = [ elem1, elem2 ] + return elements + } + + private func failure() -> XML.Error { + return XML.Error.AccessError(description: "error") + } +} diff --git a/SwiftyXMLParserTests/BrokenXMLDocument.xml b/SwiftyXMLParserTests/BrokenXMLDocument.xml new file mode 100755 index 0000000..f3c7dfd --- /dev/null +++ b/SwiftyXMLParserTests/BrokenXMLDocument.xml @@ -0,0 +1,8 @@ + + + + + ノートパソコン + 送料無料!延長保証受付中! + 14型ワイド ノートPC + \ No newline at end of file diff --git a/SwiftyXMLParserTests/CustomOperatorTests.swift b/SwiftyXMLParserTests/CustomOperatorTests.swift new file mode 100755 index 0000000..db6b47f --- /dev/null +++ b/SwiftyXMLParserTests/CustomOperatorTests.swift @@ -0,0 +1,55 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import UIKit +import XCTest +@testable import SwiftyXMLParser + +class CustomOperatorTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + // MAKR:- ?=のテスト + func testFailableAssignment() { + let optionalSome: Int? = 1 + var targetSome: Int = 0 + targetSome ?= optionalSome + XCTAssertEqual(targetSome, 1, "assign if not Optional") + + + let optionalNone: Int? = nil + var targetNone: Int = 0 + targetNone ?= optionalNone + XCTAssertEqual(targetNone, 0, "go through if Optional") + } + +} diff --git a/SwiftyXMLParserTests/Info.plist b/SwiftyXMLParserTests/Info.plist new file mode 100755 index 0000000..ba72822 --- /dev/null +++ b/SwiftyXMLParserTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/SwiftyXMLParserTests/ParserTests.swift b/SwiftyXMLParserTests/ParserTests.swift new file mode 100755 index 0000000..861c14d --- /dev/null +++ b/SwiftyXMLParserTests/ParserTests.swift @@ -0,0 +1,148 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import UIKit +import XCTest +@testable import SwiftyXMLParser + + +class ParserTests: XCTestCase { + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testSuccessParse() { + guard let path = NSBundle(forClass: self.dynamicType).pathForResource("XMLDocument", ofType: "xml"), + let data = NSData(contentsOfFile: path) else { + XCTFail("fail to parse") + return + } + + let xml = XML.Parser().parse(data) + if let _ = xml["ResultSet"].error { + XCTFail("fail to parse") + } else { + XCTAssert(true, "success to parse") + } + + + } + + func testFailParse() { + guard let path = NSBundle(forClass: self.dynamicType).pathForResource("BrokenXMLDocument", ofType: "xml"), + let data = NSData(contentsOfFile: path) else { + XCTFail("fail to parse") + return + } + + let xml = XML.Parser().parse(data) + if let _ = xml["ResultSet"].error { + XCTFail("fail to parse") + } else { + XCTAssert(true, "success to parse") + } + } + + func testTextParseWithMockData() { + guard let path = NSBundle(forClass: self.dynamicType).pathForResource("SimpleDocument", ofType: "xml"), + let data = NSData(contentsOfFile: path) else { + XCTFail("fail to parse") + return + } + + let xml = XML.Parser().parse(data) + if let text = xml["Result", "Text"].text { + XCTAssertEqual("Text", text, "Parsed Text") + } else { + XCTAssert(true, "fail to parse") + } + } + + func testWhitespaceParseWithMockData() { + guard let path = NSBundle(forClass: self.dynamicType).pathForResource("SimpleDocument", ofType: "xml"), + let data = NSData(contentsOfFile: path) else { + XCTFail("fail to parse") + return + } + + let xml = XML.Parser().parse(data) + if let text = xml["Result", "Whitespace"].text { + XCTAssertEqual(" ", text, "Parsed Single-Bite Whitespace") + } else { + XCTFail("fail") + } + } + + func testReturnParseWithMockData() { + guard let path = NSBundle(forClass: self.dynamicType).pathForResource("SimpleDocument", ofType: "xml"), + let data = NSData(contentsOfFile: path) else { + XCTFail("fail to parse") + return + } + + let xml = XML.Parser().parse(data) + if let text = xml["Result", "OnlyReturn"].text { + XCTAssertEqual("\n", text, "Parsed line break code") + } else { + XCTAssert(false, "need to have no line break code") + } + } + + func testWhitespaceAndReturnParseWithMockData() { + guard let path = NSBundle(forClass: self.dynamicType).pathForResource("SimpleDocument", ofType: "xml"), + let data = NSData(contentsOfFile: path) else { + XCTFail("fail to parse") + return + } + + let xml = XML.Parser().parse(data) + if let text = xml["Result", "WhitespaceReturn"].text { + XCTAssertEqual("\n \n", text, "Parsed whitespace and line break code") + } else { + XCTAssert(false, "need to have no line break code") + + } + } + + func testWhitespaceAndReturnParseWithMockDataAndTrimmingWhitespaceAndLineBreak() { + guard let path = NSBundle(forClass: self.dynamicType).pathForResource("SimpleDocument", ofType: "xml"), + let data = NSData(contentsOfFile: path) else { + XCTFail("fail to parse") + return + } + + + let xml = XML.Parser(trimming: .whitespaceAndNewlineCharacterSet()).parse(data) + if let text = xml["Result", "WhitespaceReturn"].text { + XCTAssertEqual("", text, "Parsed Success and trim them") + } else { + XCTAssert(false, "fail") + } + } +} \ No newline at end of file diff --git a/SwiftyXMLParserTests/SimpleDocument.xml b/SwiftyXMLParserTests/SimpleDocument.xml new file mode 100755 index 0000000..c09b7cf --- /dev/null +++ b/SwiftyXMLParserTests/SimpleDocument.xml @@ -0,0 +1,7 @@ + + Text + + + + + \ No newline at end of file diff --git a/SwiftyXMLParserTests/SwiftyXMLParserTests.swift b/SwiftyXMLParserTests/SwiftyXMLParserTests.swift new file mode 100755 index 0000000..f6eb335 --- /dev/null +++ b/SwiftyXMLParserTests/SwiftyXMLParserTests.swift @@ -0,0 +1,52 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import XCTest +@testable import SwiftyXMLParser + +class SwiftyXMLParserTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measureBlock { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/SwiftyXMLParserTests/XMLDocument.xml b/SwiftyXMLParserTests/XMLDocument.xml new file mode 100755 index 0000000..871c5d7 --- /dev/null +++ b/SwiftyXMLParserTests/XMLDocument.xml @@ -0,0 +1,20 @@ + + + + + ノートパソコン + 送料無料!延長保証受付中! + 14型ワイド ノートPC + + + ノートパソコン + 送料無料!延長保証受付中! + 14型ワイド ノートPC + + + ノートパソコン + 送料無料!延長保証受付中! + 14型ワイド ノートPC + + + \ No newline at end of file diff --git a/SwiftyXMLParserTests/XMLTests.swift b/SwiftyXMLParserTests/XMLTests.swift new file mode 100755 index 0000000..61a309f --- /dev/null +++ b/SwiftyXMLParserTests/XMLTests.swift @@ -0,0 +1,86 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 Yahoo Japan Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import UIKit +import XCTest +@testable import SwiftyXMLParser + +class XMLTests: XCTestCase { + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testParse() { + if let path = NSBundle(forClass: self.dynamicType).pathForResource("XMLDocument", ofType: "xml") { + if let data = NSData(contentsOfFile: path) { + let xml = XML.parse(data) + if let _ = xml["ResultSet"].error { + XCTFail("fail to parse") + + } else { + XCTAssert(true, "sucess to Parse") + } + } else { + XCTFail("fail to generate data") + } + } else { + XCTFail("fail to parse") + } + } + + + func testSuccessParseFromString() { + if let path = NSBundle(forClass: self.dynamicType).pathForResource("XMLDocument", ofType: "xml"), + let string = try? String(contentsOfFile: path, encoding: NSUTF8StringEncoding), + let xml = try? XML.parse(string) { + if let _ = xml["ResultSet"].error { + XCTFail("fail to parse") + } else { + XCTAssert(true, "success to parse") + } + } else { + XCTFail("fail to parse") + } + } + + + func testSuccessParseFromDoublebyteSpace() { + guard let xml = try? XML.parse(" ") else { + XCTFail("Fail Prase") + return + } + + if let name = xml["Name"].text { + XCTAssertEqual(name, " ", "Parse Success Double-byte Space") + } else { + XCTFail("Fail Prase") + } + } + +}