Skip to content

Commit ad5eeea

Browse files
authored
Merge pull request #64 from mrotrifork/feature/edit-xml
Feature: Set text, append element and ignore namespaces
2 parents a188a1c + 109912c commit ad5eeea

File tree

9 files changed

+331
-126
lines changed

9 files changed

+331
-126
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,7 @@ DerivedData/
108108
!default.perspectivev3
109109

110110

111+
112+
SwiftyXMLParser.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
113+
114+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

SwiftyXMLParser/Accessor.swift

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -128,23 +128,26 @@ extension XML {
128128
let accessor: Accessor
129129
switch self {
130130
case .singleElement(let element):
131-
let filterdElements = element.childElements.filter { $0.name == key }
132-
if filterdElements.isEmpty {
131+
let childElements = element.childElements.filter {
132+
if $0.ignoreNamespaces {
133+
return key == $0.name.components(separatedBy: ":").last ?? $0.name
134+
} else {
135+
return key == $0.name
136+
}
137+
}
138+
if childElements.isEmpty {
133139
let error = accessError("\(key) not found.")
134-
accessor = Accessor(error)
135-
} else if filterdElements.count == 1 {
136-
accessor = Accessor(filterdElements[0])
140+
accessor = Accessor(error)
141+
} else if childElements.count == 1 {
142+
accessor = Accessor(childElements[0])
137143
} else {
138-
accessor = Accessor(filterdElements)
144+
accessor = Accessor(childElements)
139145
}
140146
case .failure(let error):
141-
accessor = Accessor(error)
142-
case .sequence(_):
143-
fallthrough
147+
accessor = Accessor(error)
144148
default:
145149
let error = accessError("cannot access \(key), because of multiple elements")
146-
accessor = Accessor(error)
147-
break
150+
accessor = Accessor(error)
148151
}
149152
return accessor
150153
}
@@ -235,22 +238,27 @@ extension XML {
235238
}
236239
return name
237240
}
238-
241+
242+
/// get and set text on single element
239243
public var text: String? {
240-
let text: String?
241-
switch self {
242-
case .singleElement(let element):
243-
text = element.text
244-
case .failure(_), .sequence(_):
245-
fallthrough
246-
default:
247-
text = nil
248-
break
244+
get {
245+
switch self {
246+
case .singleElement(let element):
247+
return element.text
248+
default:
249+
return nil
250+
}
251+
}
252+
set {
253+
switch self {
254+
case .singleElement(let element):
255+
element.text = newValue
256+
default:
257+
break
258+
}
249259
}
250-
return text
251260
}
252261

253-
254262
/// syntax sugar to access Bool Text
255263
public var bool: Bool? {
256264
return text.flatMap { $0 == "true" }
@@ -272,19 +280,24 @@ extension XML {
272280
return text.flatMap({Double($0)})
273281
}
274282

275-
/// access to XML Attributes
283+
/// get and set XML attributes on single element
276284
public var attributes: [String: String] {
277-
let attributes: [String: String]
278-
switch self {
279-
case .singleElement(let element):
280-
attributes = element.attributes
281-
case .failure(_), .sequence(_):
282-
fallthrough
283-
default:
284-
attributes = [String: String]()
285-
break
285+
get {
286+
switch self {
287+
case .singleElement(let element):
288+
return element.attributes
289+
default:
290+
return [String: String]()
291+
}
292+
}
293+
set {
294+
switch self {
295+
case .singleElement(let element):
296+
element.attributes = newValue
297+
default:
298+
break
299+
}
286300
}
287-
return attributes
288301
}
289302

290303
/// access to child Elements
@@ -396,6 +409,15 @@ extension XML {
396409
}
397410
}
398411

412+
public func append(_ newElement: Element) {
413+
switch self {
414+
case .singleElement(let element):
415+
element.childElements.append(newElement)
416+
default:
417+
break
418+
}
419+
}
420+
399421
// MARK: - SequenceType
400422

401423
public func makeIterator() -> AnyIterator<Accessor> {
@@ -451,7 +473,7 @@ extension XML {
451473
}
452474

453475
extension XML {
454-
/// Conveter to make xml document from Accessor.
476+
/// Converter to make xml document from Accessor.
455477
public class Converter {
456478
let accessor: XML.Accessor
457479

@@ -460,7 +482,9 @@ extension XML {
460482
}
461483

462484
/**
463-
If Accessor object has correct XML path, return the XML element, otherwith return error
485+
Convert accessor back to XML document string.
486+
487+
- Parameter withDeclaration:Prefix with standard XML declaration (default true)
464488

465489
example:
466490

@@ -473,12 +497,12 @@ extension XML {
473497
```
474498

475499
*/
476-
public func makeDocument() throws -> String {
500+
public func makeDocument(withDeclaration: Bool = true) throws -> String {
477501
if case .failure(let err) = accessor {
478502
throw err
479503
}
480504

481-
var doc: String = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
505+
var doc = withDeclaration ? "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" : ""
482506
for hit in accessor {
483507
switch hit {
484508
case .singleElement(let element):
@@ -496,7 +520,7 @@ extension XML {
496520
private func traverse(_ element: Element) -> String {
497521
let name = element.name
498522
let text = element.text ?? ""
499-
let attrs = element.attributes.map { (k, v) in "\(k)=\"\(v)\"" }.joined(separator: " ")
523+
let attrs = element.attributes.map { (k, v) in "\(k)=\"\(v)\"" }.joined(separator: " ")
500524

501525
let childDocs = element.childElements.reduce("", { (result, element) in
502526
result + traverse(element)
@@ -505,7 +529,7 @@ extension XML {
505529
if name == "XML.Parser.AbstructedDocumentRoot" {
506530
return childDocs
507531
} else {
508-
return "<\(name) \(attrs)>\(text)\(childDocs)</\(name)>"
532+
return "<\(name)\(attrs.isEmpty ? "" : " ")\(attrs)>\(text)\(childDocs)</\(name)>"
509533
}
510534
}
511535
}

SwiftyXMLParser/Element.swift

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,34 @@ extension XML {
2828
open class Element {
2929
open var name: String
3030
open var text: String?
31-
open var attributes = [String: String]()
32-
open var childElements = [Element]()
33-
open var lineNumberStart = -1
34-
open var lineNumberEnd = -1
31+
open var attributes: [String: String]
32+
open var childElements: [Element]
33+
open var lineNumberStart: Int
34+
open var lineNumberEnd: Int
3535
open var CDATA: Data?
36+
open var ignoreNamespaces: Bool
3637

3738
// for println
3839
open weak var parentElement: Element?
39-
40-
public init(name: String) {
40+
41+
public init(
42+
name: String,
43+
text: String? = nil,
44+
attributes: [String: String] = [:],
45+
childElements: [Element] = [],
46+
lineNumberStart: Int = -1,
47+
lineNumberEnd: Int = -1,
48+
CDATA: Data? = nil,
49+
ignoreNamespaces: Bool = false
50+
) {
4151
self.name = name
52+
self.text = text
53+
self.attributes = attributes
54+
self.childElements = childElements
55+
self.lineNumberStart = lineNumberStart
56+
self.lineNumberEnd = lineNumberEnd
57+
self.CDATA = CDATA
58+
self.ignoreNamespaces = ignoreNamespaces
4259
}
4360
}
4461
}

SwiftyXMLParser/Parser.swift

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ extension XML {
3333
/// So the result of parsing is missing.
3434
/// See https://developer.apple.com/documentation/foundation/xmlparser/errorcode
3535
private(set) var error: XMLError?
36-
36+
3737
func parse(_ data: Data) -> Accessor {
3838
stack = [Element]()
3939
stack.append(documentRoot)
@@ -46,22 +46,21 @@ extension XML {
4646
return Accessor(documentRoot)
4747
}
4848
}
49-
50-
override init() {
51-
trimmingManner = nil
52-
}
53-
54-
init(trimming manner: CharacterSet) {
55-
trimmingManner = manner
49+
50+
init(trimming manner: CharacterSet? = nil, ignoreNamespaces: Bool = false) {
51+
self.trimmingManner = manner
52+
self.ignoreNamespaces = ignoreNamespaces
53+
self.documentRoot = Element(name: "XML.Parser.AbstructedDocumentRoot", ignoreNamespaces: ignoreNamespaces)
5654
}
5755

5856
// MARK:- private
59-
fileprivate var documentRoot = Element(name: "XML.Parser.AbstructedDocumentRoot")
57+
fileprivate var documentRoot: Element
6058
fileprivate var stack = [Element]()
6159
fileprivate let trimmingManner: CharacterSet?
60+
fileprivate let ignoreNamespaces: Bool
6261

6362
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
64-
let node = Element(name: elementName)
63+
let node = Element(name: elementName, ignoreNamespaces: ignoreNamespaces)
6564
node.lineNumberStart = parser.lineNumber
6665
if !attributeDict.isEmpty {
6766
node.attributes = attributeDict

SwiftyXMLParser/XML.swift

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -86,57 +86,43 @@ public func ?<< <T>(lhs: inout [T], rhs: T?) {
8686
```
8787
*/
8888
open class XML {
89+
8990
/**
90-
Interface to parse NSData
91-
92-
- parameter data:NSData XML document
93-
- returns:Accessor object to access XML document
94-
*/
95-
open class func parse(_ data: Data) -> Accessor {
96-
return Parser().parse(data)
97-
}
98-
99-
/**
100-
Interface to parse String
101-
102-
- Parameter str:String XML document
103-
- Returns:Accessor object to access XML document
104-
*/
105-
open class func parse(_ str: String) throws -> Accessor {
106-
guard let data = str.data(using: String.Encoding.utf8) else {
107-
throw XMLError.failToEncodeString
108-
}
109-
110-
return Parser().parse(data)
111-
}
112-
113-
/**
114-
Interface to parse NSData
91+
Interface to parse Data
11592

116-
- parameter data:NSData XML document
117-
- parameter manner:NSCharacterSet If you wannna trim Text, assign this arg
93+
- parameter data:Data XML document
94+
- parameter manner:CharacterSet If you want to trim text (default off)
95+
- parameter ignoreNamespaces:Bool If set to true all accessors will ignore the first part of an element name up to a semicolon (default false)
11896
- returns:Accessor object to access XML document
11997
*/
120-
open class func parse(_ data: Data, trimming manner: CharacterSet) -> Accessor {
121-
return Parser(trimming: manner).parse(data)
98+
open class func parse(_ data: Data, trimming manner: CharacterSet? = nil, ignoreNamespaces: Bool = false) -> Accessor {
99+
return Parser(trimming: manner, ignoreNamespaces: ignoreNamespaces).parse(data)
122100
}
123101

124102
/**
125103
Interface to parse String
126104

127-
- Parameter str:String XML document
128-
- parameter manner:NSCharacterSet If you wannna trim Text, assign this arg
129-
- Returns:Accessor object to access XML document
105+
- parameter str:String XML document
106+
- parameter manner:CharacterSet If you want to trim text (default off)
107+
- parameter ignoreNamespaces:Bool If set to true all accessors will ignore the first part of an element name up to a semicolon (default false)
108+
- returns:Accessor object to access XML document
130109
*/
131-
open class func parse(_ str: String, trimming manner: CharacterSet) throws -> Accessor {
110+
open class func parse(_ str: String, trimming manner: CharacterSet? = nil, ignoreNamespaces: Bool = false) throws -> Accessor {
132111
guard let data = str.data(using: String.Encoding.utf8) else {
133112
throw XMLError.failToEncodeString
134113
}
135114

136-
return Parser(trimming: manner).parse(data)
115+
return Parser(trimming: manner, ignoreNamespaces: ignoreNamespaces).parse(data)
137116
}
138-
139-
open class func document(_ accessor: Accessor) throws -> String {
140-
return try Converter(accessor).makeDocument()
117+
118+
/**
119+
Convert accessor back to XML document string.
120+
121+
- parameter accessor:XML accessor
122+
- parameter withDeclaration:Prefix with standard XML declaration (default true)
123+
- returns:XML document string
124+
*/
125+
open class func document(_ accessor: Accessor, withDeclaration: Bool = true) throws -> String {
126+
return try Converter(accessor).makeDocument(withDeclaration: withDeclaration)
141127
}
142128
}

0 commit comments

Comments
 (0)