Skip to content

Commit 4b08c15

Browse files
authored
Merge pull request #35 from yahoojapan/feature/makeDocument
generate XML Docuemnt
2 parents 13c1b5e + 584d461 commit 4b08c15

File tree

5 files changed

+208
-2
lines changed

5 files changed

+208
-2
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,12 @@ entity.names ?<< xml.ResultSet.Result.Hit[1].Name.text // assign if it has text
226226
```swift
227227
let numberOfHits = xml.ResultSet.Result.Hit.all?.count
228228
```
229-
### Check error
229+
### 8. Check error
230230
```swift
231231
print(xml.ResultSet.Result.TypoKey) // -> "TypoKey not found."
232232
```
233233

234-
### Access as SequenceType
234+
### 9. Access as SequenceType
235235
+ for-in
236236
```swift
237237
for element in xml.ResultSet.Result.Hit {
@@ -243,6 +243,12 @@ for element in xml.ResultSet.Result.Hit {
243243
xml.ResultSet.Result.Hit.map { $0.Name.text }
244244
```
245245

246+
### 9. Generate XML document
247+
```swift
248+
print(Converter(xml.ResultSet).makeDocument())
249+
```
250+
251+
246252
## Work with Alamofire
247253
SwiftyXMLParser goes well with [Alamofire](https://github.com/Alamofire/Alamofire). You can parse the response easily.
248254

SwiftyXMLParser.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
2BD943341CB22107007D5FFC /* BrokenXMLDocument.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2BD943331CB22107007D5FFC /* BrokenXMLDocument.xml */; };
2323
2BD943361CB2210D007D5FFC /* XMLDocument.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2BD943351CB2210D007D5FFC /* XMLDocument.xml */; };
2424
2BD943381CB23CFE007D5FFC /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD943371CB23CFE007D5FFC /* Error.swift */; };
25+
974D50012243BB0E0015B768 /* ConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974D50002243BB0E0015B768 /* ConverterTests.swift */; };
2526
/* End PBXBuildFile section */
2627

2728
/* Begin PBXContainerItemProxy section */
@@ -54,6 +55,7 @@
5455
2BD943351CB2210D007D5FFC /* XMLDocument.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = XMLDocument.xml; sourceTree = "<group>"; };
5556
2BD943371CB23CFE007D5FFC /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
5657
81B640ED1FC77E6400BE1AB5 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
58+
974D50002243BB0E0015B768 /* ConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConverterTests.swift; sourceTree = "<group>"; };
5759
/* End PBXFileReference section */
5860

5961
/* Begin PBXFrameworksBuildPhase section */
@@ -114,6 +116,7 @@
114116
2BD943321CB220FA007D5FFC /* Fixture */,
115117
2B6D92421C7F0587000D2D06 /* SwiftyXMLParserTests.swift */,
116118
2BD9432A1CB220E0007D5FFC /* XMLTests.swift */,
119+
974D50002243BB0E0015B768 /* ConverterTests.swift */,
117120
2BD9432C1CB220E5007D5FFC /* ParserTests.swift */,
118121
2BD9432E1CB220EA007D5FFC /* AccessorTests.swift */,
119122
2BD943301CB220F1007D5FFC /* CustomOperatorTests.swift */,
@@ -258,6 +261,7 @@
258261
isa = PBXSourcesBuildPhase;
259262
buildActionMask = 2147483647;
260263
files = (
264+
974D50012243BB0E0015B768 /* ConverterTests.swift in Sources */,
261265
2BD943311CB220F1007D5FFC /* CustomOperatorTests.swift in Sources */,
262266
2BD9432F1CB220EA007D5FFC /* AccessorTests.swift in Sources */,
263267
2B6D92431C7F0587000D2D06 /* SwiftyXMLParserTests.swift in Sources */,

SwiftyXMLParser/Accessor.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,64 @@ extension XML {
449449
}
450450
}
451451
}
452+
453+
extension XML {
454+
/// Conveter to make xml document from Accessor.
455+
public class Converter {
456+
let accessor: XML.Accessor
457+
458+
public init(_ accessor: XML.Accessor) {
459+
self.accessor = accessor
460+
}
461+
462+
/**
463+
If Accessor object has correct XML path, return the XML element, otherwith return error
464+
465+
example:
466+
467+
```
468+
let xml = try! XML.parse("<?xml version="1.0" encoding="UTF-8"?><doc><name key="value">text</name></doc>")
469+
let elem = xml.doc
470+
471+
print(Converter(elem).makeDocument())
472+
// => <?xml version="1.0" encoding="UTF-8"?><name key="value">text</name>
473+
```
474+
475+
*/
476+
public func makeDocument() throws -> String {
477+
if case .failure(let err) = accessor {
478+
throw err
479+
}
480+
481+
var doc: String = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
482+
for hit in accessor {
483+
switch hit {
484+
case .singleElement(let element):
485+
doc += traverse(element)
486+
case .sequence(let elements):
487+
doc += elements.reduce("") { (sum, el) in sum + traverse(el) }
488+
case .failure(let error):
489+
throw error
490+
}
491+
}
492+
493+
return doc
494+
}
495+
496+
private func traverse(_ element: Element) -> String {
497+
let name = element.name
498+
let text = element.text ?? ""
499+
let attrs = element.attributes.map { (k, v) in "\(k)=\"\(v)\"" }.joined(separator: " ")
500+
501+
let childDocs = element.childElements.reduce("", { (result, element) in
502+
result + traverse(element)
503+
})
504+
505+
if name == "XML.Parser.AbstructedDocumentRoot" {
506+
return childDocs
507+
} else {
508+
return "<\(name) \(attrs)>\(text)\(childDocs)</\(name)>"
509+
}
510+
}
511+
}
512+
}

SwiftyXMLParser/XML.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,8 @@ open class XML {
135135

136136
return Parser(trimming: manner).parse(data)
137137
}
138+
139+
open class func document(_ accessor: Accessor) throws -> String {
140+
return try Converter(accessor).makeDocument()
141+
}
138142
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (C) 2016 Yahoo Japan Corporation.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
import XCTest
26+
import SwiftyXMLParser
27+
28+
class ConverterTests: XCTestCase {
29+
func testMakeDosument() {
30+
// no chiled element, text only
31+
do {
32+
let element = XML.Element(name: "name",
33+
text: "text",
34+
attributes: ["key": "value"])
35+
let converter = XML.Converter(XML.Accessor(element))
36+
37+
guard let result = try? converter.makeDocument() else {
38+
XCTFail("fail to make document")
39+
return
40+
}
41+
let extpected = """
42+
<?xml version="1.0" encoding="UTF-8"?><name key="value">text</name>
43+
"""
44+
XCTAssertEqual(result, extpected)
45+
}
46+
47+
// no text, chiled elements only
48+
do {
49+
let childElements = [
50+
XML.Element(name: "c_name1", text: "c_text1", attributes: ["c_key1": "c_value1"]),
51+
XML.Element(name: "c_name2", text: "c_text2", attributes: ["c_key2": "c_value2"])
52+
]
53+
let element = XML.Element(name: "name",
54+
text: nil,
55+
attributes: ["key": "value"],
56+
childElements: childElements)
57+
58+
let converter = XML.Converter(XML.Accessor(element))
59+
guard let result = try? converter.makeDocument() else {
60+
XCTFail("fail to make document")
61+
return
62+
}
63+
let extpected = """
64+
<?xml version="1.0" encoding="UTF-8"?><name key="value"><c_name1 c_key1="c_value1">c_text1</c_name1><c_name2 c_key2="c_value2">c_text2</c_name2></name>
65+
"""
66+
XCTAssertEqual(result, extpected)
67+
}
68+
69+
// both text and chiled element
70+
do {
71+
let childElements = [
72+
XML.Element(name: "c_name1", text: "c_text1", attributes: ["c_key1": "c_value1"]),
73+
XML.Element(name: "c_name2", text: "c_text2", attributes: ["c_key2": "c_value2"])
74+
]
75+
let element = XML.Element(name: "name",
76+
text: "text",
77+
attributes: ["key": "value"],
78+
childElements: childElements)
79+
let converter = XML.Converter(XML.Accessor(element))
80+
guard let result = try? converter.makeDocument() else {
81+
XCTFail("fail to make document")
82+
return
83+
}
84+
let extpected = """
85+
<?xml version="1.0" encoding="UTF-8"?><name key="value">text<c_name1 c_key1="c_value1">c_text1</c_name1><c_name2 c_key2="c_value2">c_text2</c_name2></name>
86+
"""
87+
XCTAssertEqual(result, extpected)
88+
}
89+
90+
// nested child elements
91+
do {
92+
let grateGrandchildElements = [
93+
XML.Element(name: "ggc_name1", text: "ggc_text1", attributes: ["ggc_key1": "ggc_value1"])
94+
]
95+
96+
let grandchildElements = [
97+
XML.Element(name: "gc_name1", text: "gc_text1", attributes: ["gc_key1": "gc_value1"], childElements: grateGrandchildElements)
98+
]
99+
100+
let childElements = [
101+
XML.Element(name: "c_name1", text: "c_text1", attributes: ["c_key1": "c_value1"]),
102+
XML.Element(name: "c_name2", text: "c_text2", attributes: ["c_key2": "c_value2"], childElements: grandchildElements)
103+
]
104+
let element = XML.Element(name: "name",
105+
text: "text",
106+
attributes: ["key": "value"],
107+
childElements: childElements)
108+
let converter = XML.Converter(XML.Accessor(element))
109+
guard let result = try? converter.makeDocument() else {
110+
XCTFail("fail to make document")
111+
return
112+
}
113+
let extpected = """
114+
<?xml version="1.0" encoding="UTF-8"?><name key="value">text<c_name1 c_key1="c_value1">c_text1</c_name1><c_name2 c_key2="c_value2">c_text2<gc_name1 gc_key1="gc_value1">gc_text1<ggc_name1 ggc_key1="ggc_value1">ggc_text1</ggc_name1></gc_name1></c_name2></name>
115+
"""
116+
XCTAssertEqual(result, extpected)
117+
}
118+
}
119+
}
120+
121+
extension XML.Element {
122+
convenience init(name: String,
123+
text: String? = nil,
124+
attributes: [String: String] = [String: String](),
125+
childElements: [XML.Element] = [XML.Element]()) {
126+
self.init(name: name)
127+
self.text = text
128+
self.attributes = attributes
129+
self.childElements = childElements
130+
}
131+
}

0 commit comments

Comments
 (0)