Skip to content

Commit a90a62e

Browse files
authored
Merge pull request #171 from drmohundro/add-userinfo-support
Add userInfo support
2 parents 6d2b827 + a2511c1 commit a90a62e

File tree

3 files changed

+92
-12
lines changed

3 files changed

+92
-12
lines changed

Source/SWXMLHash.swift

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public class SWXMLHashOptions {
5050

5151
/// Encoding used for XML parsing. Default is set to UTF8
5252
public var encoding = String.Encoding.utf8
53+
54+
/// Any contextual information set by the user for encoding
55+
public var userInfo = [CodingUserInfoKey: Any]()
5356
}
5457

5558
/// Simple XML parser
@@ -250,12 +253,12 @@ extension XMLParserDelegate {
250253
/// The implementation of XMLParserDelegate and where the lazy parsing actually happens.
251254
class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
252255
required init(_ options: SWXMLHashOptions) {
256+
root = XMLElement(name: rootElementName, options: options)
253257
self.options = options
254-
self.root.caseInsensitive = options.caseInsensitive
255258
super.init()
256259
}
257260

258-
var root = XMLElement(name: rootElementName, caseInsensitive: false)
261+
let root: XMLElement
259262
var parentStack = Stack<XMLElement>()
260263
var elementStack = Stack<String>()
261264

@@ -272,7 +275,6 @@ class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
272275
// clear any prior runs of parse... expected that this won't be necessary,
273276
// but you never know
274277
parentStack.removeAll()
275-
root = XMLElement(name: rootElementName, caseInsensitive: options.caseInsensitive)
276278
parentStack.push(root)
277279

278280
self.ops = ops
@@ -338,12 +340,12 @@ class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
338340
/// The implementation of XMLParserDelegate and where the parsing actually happens.
339341
class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
340342
required init(_ options: SWXMLHashOptions) {
343+
root = XMLElement(name: rootElementName, options: options)
341344
self.options = options
342-
self.root.caseInsensitive = options.caseInsensitive
343345
super.init()
344346
}
345347

346-
var root = XMLElement(name: rootElementName, caseInsensitive: false)
348+
let root: XMLElement
347349
var parentStack = Stack<XMLElement>()
348350
let options: SWXMLHashOptions
349351

@@ -556,6 +558,15 @@ public enum XMLIndexer {
556558
return list
557559
}
558560

561+
public var userInfo: [CodingUserInfoKey: Any] {
562+
switch self {
563+
case .element(let elem):
564+
return elem.userInfo
565+
default:
566+
return [:]
567+
}
568+
}
569+
559570
/**
560571
Allows for element lookup by matching attribute values.
561572

@@ -775,11 +786,19 @@ public class XMLElement: XMLContent {
775786
/// The name of the element
776787
public let name: String
777788

778-
public var caseInsensitive: Bool
789+
/// Whether the element is case insensitive or not
790+
public var caseInsensitive: Bool {
791+
return options.caseInsensitive
792+
}
793+
794+
var userInfo: [CodingUserInfoKey: Any] {
795+
return options.userInfo
796+
}
779797

780798
/// All attributes
781799
public var allAttributes = [String: XMLAttribute]()
782800

801+
/// Find an attribute by name
783802
public func attribute(by name: String) -> XMLAttribute? {
784803
if caseInsensitive {
785804
return allAttributes.first(where: { $0.key.compare(name, true) })?.value
@@ -813,8 +832,10 @@ public class XMLElement: XMLContent {
813832

814833
/// All child elements (text or XML)
815834
public var children = [XMLContent]()
835+
816836
var count: Int = 0
817837
var index: Int
838+
let options: SWXMLHashOptions
818839

819840
var xmlChildren: [XMLElement] {
820841
return children.flatMap { $0 as? XMLElement }
@@ -827,10 +848,10 @@ public class XMLElement: XMLContent {
827848
- name: The name of the element to be initialized
828849
- index: The index of the element to be initialized
829850
*/
830-
init(name: String, index: Int = 0, caseInsensitive: Bool) {
851+
init(name: String, index: Int = 0, options: SWXMLHashOptions) {
831852
self.name = name
832-
self.caseInsensitive = caseInsensitive
833853
self.index = index
854+
self.options = options
834855
}
835856

836857
/**
@@ -843,7 +864,7 @@ public class XMLElement: XMLContent {
843864
*/
844865

845866
func addElement(_ name: String, withAttributes attributes: [String: String], caseInsensitive: Bool) -> XMLElement {
846-
let element = XMLElement(name: name, index: count, caseInsensitive: caseInsensitive)
867+
let element = XMLElement(name: name, index: count, options: options)
847868
count += 1
848869

849870
children.append(element)

Tests/SWXMLHashTests/LazyTypesConversionTests.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,28 @@ class LazyTypesConversionTests: XCTestCase {
6666
XCTFail("\(error)")
6767
}
6868
}
69+
70+
func testShouldBeAbleToGetUserInfoDuringDeserialization() {
71+
parser = SWXMLHash.config { config in
72+
let options = SampleUserInfo(apiVersion: .v1)
73+
config.userInfo = [ SampleUserInfo.key: options ]
74+
}.parse(xmlWithBasicTypes)
75+
76+
do {
77+
let value: BasicItem = try parser!["root"]["basicItem"].value()
78+
XCTAssertEqual(value.name, "the name of basic item (v1)")
79+
} catch {
80+
XCTFail("\(error)")
81+
}
82+
}
6983
}
7084

7185
extension LazyTypesConversionTests {
7286
static var allTests: [(String, (LazyTypesConversionTests) -> () throws -> Void)] {
7387
return [
7488
("testShouldConvertValueToNonOptional", testShouldConvertValueToNonOptional),
75-
("testShouldConvertAttributeToNonOptional", testShouldConvertAttributeToNonOptional)
89+
("testShouldConvertAttributeToNonOptional", testShouldConvertAttributeToNonOptional),
90+
("testShouldBeAbleToGetUserInfoDuringDeserialization", testShouldBeAbleToGetUserInfoDuringDeserialization)
7691
]
7792
}
7893
}

Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,29 @@ import XCTest
3131
// swiftlint:disable line_length
3232
// swiftlint:disable type_body_length
3333

34+
struct SampleUserInfo {
35+
enum ApiVersion {
36+
case v1
37+
case v2
38+
}
39+
40+
var apiVersion = ApiVersion.v2
41+
42+
func suffix() -> String {
43+
if apiVersion == ApiVersion.v1 {
44+
return " (v1)"
45+
} else {
46+
return ""
47+
}
48+
}
49+
50+
static let key = CodingUserInfoKey(rawValue: "test")!
51+
52+
init(apiVersion: ApiVersion) {
53+
self.apiVersion = apiVersion
54+
}
55+
}
56+
3457
class TypeConversionBasicTypesTests: XCTestCase {
3558
var parser: XMLIndexer?
3659
let xmlWithBasicTypes = """
@@ -524,15 +547,35 @@ class TypeConversionBasicTypesTests: XCTestCase {
524547
XCTFail("\(error)")
525548
}
526549
}
550+
551+
func testShouldBeAbleToGetUserInfoDuringDeserialization() {
552+
parser = SWXMLHash.config { config in
553+
let options = SampleUserInfo(apiVersion: .v1)
554+
config.userInfo = [ SampleUserInfo.key: options ]
555+
}.parse(xmlWithBasicTypes)
556+
557+
do {
558+
let value: BasicItem = try parser!["root"]["basicItem"].value()
559+
XCTAssertEqual(value.name, "the name of basic item (v1)")
560+
} catch {
561+
XCTFail("\(error)")
562+
}
563+
}
527564
}
528565

529566
struct BasicItem: XMLIndexerDeserializable {
530567
let name: String
531568
let price: Double
532569

533570
static func deserialize(_ node: XMLIndexer) throws -> BasicItem {
571+
var name: String = try node["name"].value()
572+
573+
if let opts = node.userInfo[SampleUserInfo.key] as? SampleUserInfo {
574+
name += opts.suffix()
575+
}
576+
534577
return try BasicItem(
535-
name: node["name"].value(),
578+
name: name,
536579
price: node["price"].value()
537580
)
538581
}
@@ -618,7 +661,8 @@ extension TypeConversionBasicTypesTests {
618661
("testAttributeItemShouldThrowWhenConvertingMissingToNonOptional", testAttributeItemShouldThrowWhenConvertingMissingToNonOptional),
619662
("testAttributeItemShouldConvertAttributeItemToOptional", testAttributeItemShouldConvertAttributeItemToOptional),
620663
("testAttributeItemShouldConvertEmptyToOptional", testAttributeItemShouldConvertEmptyToOptional),
621-
("testAttributeItemShouldConvertMissingToOptional", testAttributeItemShouldConvertMissingToOptional)
664+
("testAttributeItemShouldConvertMissingToOptional", testAttributeItemShouldConvertMissingToOptional),
665+
("testShouldBeAbleToGetUserInfoDuringDeserialization", testShouldBeAbleToGetUserInfoDuringDeserialization)
622666
]
623667
}
624668
}

0 commit comments

Comments
 (0)