Skip to content

Commit

Permalink
Merge pull request #35 from yahoojapan/feature/makeDocument
Browse files Browse the repository at this point in the history
generate XML Docuemnt
  • Loading branch information
kazuhiro4949 authored Oct 2, 2019
2 parents 13c1b5e + 584d461 commit 4b08c15
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 2 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,12 @@ entity.names ?<< xml.ResultSet.Result.Hit[1].Name.text // assign if it has text
```swift
let numberOfHits = xml.ResultSet.Result.Hit.all?.count
```
### Check error
### 8. Check error
```swift
print(xml.ResultSet.Result.TypoKey) // -> "TypoKey not found."
```

### Access as SequenceType
### 9. Access as SequenceType
+ for-in
```swift
for element in xml.ResultSet.Result.Hit {
Expand All @@ -243,6 +243,12 @@ for element in xml.ResultSet.Result.Hit {
xml.ResultSet.Result.Hit.map { $0.Name.text }
```

### 9. Generate XML document
```swift
print(Converter(xml.ResultSet).makeDocument())
```


## Work with Alamofire
SwiftyXMLParser goes well with [Alamofire](https://github.com/Alamofire/Alamofire). You can parse the response easily.

Expand Down
4 changes: 4 additions & 0 deletions SwiftyXMLParser.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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 */; };
974D50012243BB0E0015B768 /* ConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974D50002243BB0E0015B768 /* ConverterTests.swift */; };
/* End PBXBuildFile section */

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

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -114,6 +116,7 @@
2BD943321CB220FA007D5FFC /* Fixture */,
2B6D92421C7F0587000D2D06 /* SwiftyXMLParserTests.swift */,
2BD9432A1CB220E0007D5FFC /* XMLTests.swift */,
974D50002243BB0E0015B768 /* ConverterTests.swift */,
2BD9432C1CB220E5007D5FFC /* ParserTests.swift */,
2BD9432E1CB220EA007D5FFC /* AccessorTests.swift */,
2BD943301CB220F1007D5FFC /* CustomOperatorTests.swift */,
Expand Down Expand Up @@ -258,6 +261,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
974D50012243BB0E0015B768 /* ConverterTests.swift in Sources */,
2BD943311CB220F1007D5FFC /* CustomOperatorTests.swift in Sources */,
2BD9432F1CB220EA007D5FFC /* AccessorTests.swift in Sources */,
2B6D92431C7F0587000D2D06 /* SwiftyXMLParserTests.swift in Sources */,
Expand Down
61 changes: 61 additions & 0 deletions SwiftyXMLParser/Accessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -449,3 +449,64 @@ extension XML {
}
}
}

extension XML {
/// Conveter to make xml document from Accessor.
public class Converter {
let accessor: XML.Accessor

public init(_ accessor: XML.Accessor) {
self.accessor = accessor
}

/**
If Accessor object has correct XML path, return the XML element, otherwith return error

example:

```
let xml = try! XML.parse("<?xml version="1.0" encoding="UTF-8"?><doc><name key="value">text</name></doc>")
let elem = xml.doc

print(Converter(elem).makeDocument())
// => <?xml version="1.0" encoding="UTF-8"?><name key="value">text</name>
```

*/
public func makeDocument() throws -> String {
if case .failure(let err) = accessor {
throw err
}

var doc: String = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
for hit in accessor {
switch hit {
case .singleElement(let element):
doc += traverse(element)
case .sequence(let elements):
doc += elements.reduce("") { (sum, el) in sum + traverse(el) }
case .failure(let error):
throw error
}
}

return doc
}

private func traverse(_ element: Element) -> String {
let name = element.name
let text = element.text ?? ""
let attrs = element.attributes.map { (k, v) in "\(k)=\"\(v)\"" }.joined(separator: " ")

let childDocs = element.childElements.reduce("", { (result, element) in
result + traverse(element)
})

if name == "XML.Parser.AbstructedDocumentRoot" {
return childDocs
} else {
return "<\(name) \(attrs)>\(text)\(childDocs)</\(name)>"
}
}
}
}
4 changes: 4 additions & 0 deletions SwiftyXMLParser/XML.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,8 @@ open class XML {

return Parser(trimming: manner).parse(data)
}

open class func document(_ accessor: Accessor) throws -> String {
return try Converter(accessor).makeDocument()
}
}
131 changes: 131 additions & 0 deletions SwiftyXMLParserTests/ConverterTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
* 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
import SwiftyXMLParser

class ConverterTests: XCTestCase {
func testMakeDosument() {
// no chiled element, text only
do {
let element = XML.Element(name: "name",
text: "text",
attributes: ["key": "value"])
let converter = XML.Converter(XML.Accessor(element))

guard let result = try? converter.makeDocument() else {
XCTFail("fail to make document")
return
}
let extpected = """
<?xml version="1.0" encoding="UTF-8"?><name key="value">text</name>
"""
XCTAssertEqual(result, extpected)
}

// no text, chiled elements only
do {
let childElements = [
XML.Element(name: "c_name1", text: "c_text1", attributes: ["c_key1": "c_value1"]),
XML.Element(name: "c_name2", text: "c_text2", attributes: ["c_key2": "c_value2"])
]
let element = XML.Element(name: "name",
text: nil,
attributes: ["key": "value"],
childElements: childElements)

let converter = XML.Converter(XML.Accessor(element))
guard let result = try? converter.makeDocument() else {
XCTFail("fail to make document")
return
}
let extpected = """
<?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>
"""
XCTAssertEqual(result, extpected)
}

// both text and chiled element
do {
let childElements = [
XML.Element(name: "c_name1", text: "c_text1", attributes: ["c_key1": "c_value1"]),
XML.Element(name: "c_name2", text: "c_text2", attributes: ["c_key2": "c_value2"])
]
let element = XML.Element(name: "name",
text: "text",
attributes: ["key": "value"],
childElements: childElements)
let converter = XML.Converter(XML.Accessor(element))
guard let result = try? converter.makeDocument() else {
XCTFail("fail to make document")
return
}
let extpected = """
<?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>
"""
XCTAssertEqual(result, extpected)
}

// nested child elements
do {
let grateGrandchildElements = [
XML.Element(name: "ggc_name1", text: "ggc_text1", attributes: ["ggc_key1": "ggc_value1"])
]

let grandchildElements = [
XML.Element(name: "gc_name1", text: "gc_text1", attributes: ["gc_key1": "gc_value1"], childElements: grateGrandchildElements)
]

let childElements = [
XML.Element(name: "c_name1", text: "c_text1", attributes: ["c_key1": "c_value1"]),
XML.Element(name: "c_name2", text: "c_text2", attributes: ["c_key2": "c_value2"], childElements: grandchildElements)
]
let element = XML.Element(name: "name",
text: "text",
attributes: ["key": "value"],
childElements: childElements)
let converter = XML.Converter(XML.Accessor(element))
guard let result = try? converter.makeDocument() else {
XCTFail("fail to make document")
return
}
let extpected = """
<?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>
"""
XCTAssertEqual(result, extpected)
}
}
}

extension XML.Element {
convenience init(name: String,
text: String? = nil,
attributes: [String: String] = [String: String](),
childElements: [XML.Element] = [XML.Element]()) {
self.init(name: name)
self.text = text
self.attributes = attributes
self.childElements = childElements
}
}

0 comments on commit 4b08c15

Please sign in to comment.