Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/PerfectlySoft/Perfect-XML
XML support for Perfect.
https://github.com/PerfectlySoft/Perfect-XML
perfect server-side-swift swift xml
Last synced: 3 months ago
JSON representation
XML support for Perfect.
- Host: GitHub
- URL: https://github.com/PerfectlySoft/Perfect-XML
- Owner: PerfectlySoft
- License: apache-2.0
- Created: 2016-07-21T02:37:49.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2018-04-04T01:26:07.000Z (over 6 years ago)
- Last Synced: 2024-04-24T00:01:30.558Z (6 months ago)
- Topics: perfect, server-side-swift, swift, xml
- Language: Swift
- Homepage: https://www.perfect.org
- Size: 59.6 KB
- Stars: 16
- Watchers: 7
- Forks: 5
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- TheList - Perfect XML - XML support for Perfect. (Parsers / XML)
README
# Perfect-XML
XML & HTML parsing support for Perfect
It currently contains most of the DOM Core level 2 *read-only* APIs and includes XPath support.
## Building
Add this project as a dependency in your Package.swift file.
```
.Package(url:"https://github.com/PerfectlySoft/Perfect-XML.git", majorVersion: 3)
```## macOS Build Notes
If you receive a compile error that says the following, you need to install and link libxml2
```
note: you may be able to install libxml-2.0 using your system-packager:brew install libxml2
Compile Swift Module 'PerfectXML' (2 sources)
:1:9: note: in file included from :1:
#import "libxml2.h"
```To install and link libxml2 with homebrew, use the following two commands
```
brew install libxml2
brew link --force libxml2
```## Linux Build Notes
Ensure that you have installed libxml2-dev and pkg-config.
```
sudo apt-get install libxml2-dev pkg-config
```## Usage
Instantiate an XDocument object with your XML string
```swift
import PerfectXML
let document = XDocument(fromSource: xmlString)
```Instantiate an HTMLDocument object with your HTML string
```swift
import PerfectXML
let document = HTMLDocument(fromSource: htmlString)
```Now you can get the root node of the document structure by using the documentElement property
```swift
print(document.documentElement?.string(pretty: true))
```Each node has several important properties
- nodeValue
- nodeName
- parentNode
- childNodesEach node also has a getElementsByTagName: method that recursively searches through it and its children to return an array of all nodes that have that name. This method makes it easy to find a single value in the XML file.
```swift
import PerfectXMLlet serverResponseXML = "\n" +
"" +
" " +
" " +
" 600" +
" Invalid Request" +
" " +
" " +
" " +
""let serverResponseDocument = XDocument(fromSource: serverResponseXML)
let serverMessage = serverResponseDocument?.documentElement?.getElementsByTagName("Message").first?.nodeValue
print(serverMessage) // prints Optional("Invalid Request")
```Constructing model types from XML feeds is easy with getElementsByTagName
```swift
import PerfectXMLlet rssXML = "" +
"" +
" " +
" W3Schools Home Page" +
" http://www.w3schools.com" +
" Free web building tutorials" +
" " +
" RSS Tutorial" +
" http://www.w3schools.com/xml/xml_rss.asp" +
" New RSS tutorial on W3Schools" +
" " +
" " +
" XML Tutorial" +
" http://www.w3schools.com/xml" +
" New XML tutorial on W3Schools" +
" " +
" " +
""let rssDocument = XDocument(fromSource: rssXML)
let feedItems = rssDocument?.documentElement?.getElementsByTagName("item")
print(feedItems?.count) // prints 2
let items = feedItems?.map({ MyCustomStruct(xmlNode: $0) })
```Sometimes tag names are a bit too generic in order to meaningfully search for them, such as "title" in the example above. If we wanted to get the Channel title, link, and description, we could visit each of its children in a more deliberate way.
```swift
import PerfectXMLlet rssXML = "" +
"" +
" " +
" W3Schools Home Page" +
" http://www.w3schools.com" +
" Free web building tutorials" +
" " +
" RSS Tutorial" +
" http://www.w3schools.com/xml/xml_rss.asp" +
" New RSS tutorial on W3Schools" +
" " +
" " +
" XML Tutorial" +
" http://www.w3schools.com/xml" +
" New XML tutorial on W3Schools" +
" " +
" " +
""let rssDocument = XDocument(fromSource: rssXML)
let channelNode = rssDocument?.documentElement?.getElementsByTagName("channel").first
let channelTitle = channelNode?.childNodes.filter({ $0.nodeName == "title" }).first?.nodeValue
let channelLink = channelNode?.childNodes.filter({ $0.nodeName == "link" }).first?.nodeValue
let channelDescription = channelNode?.childNodes.filter({ $0.nodeName == "description" }).first?.nodeValueprint(channelTitle) // Optional("W3Schools Home Page")
print(channelLink) // Optional("http://www.w3schools.com")
print(channelDescription) // Optional("Free web building tutorials")
```### Parse XML Source
This snippet will parse an XML source string and then convert it back into a string.
```swift
let docSrc = "\nHI\n"
let doc = XDocument(fromSource: docSrc)
let str = doc?.string(pretty: false)
XCTAssert(str == docSrc, "\(str)")
```### Parse HTML Source
This snippet will parse an HTML source string.
```swift
let docSrc = "\n\ntitle\n\nhi\n\n\n"
let doc = HTMLDocument(fromSource: docSrc)
let nodeName = doc?.documentElement?.nodeName
XCTAssert(nodeName == "html")
```### Inspect Node Names
```swift
let docSrc = "\n\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
guard let children = doc?.documentElement else {
return XCTAssert(false, "No children")
}
XCTAssert(children.nodeName == "a")
let names = ["b", "c", "d"]
for (n, v) in zip(children.childNodes, names) {
guard let _ = n as? XElement else {
return XCTAssert(false)
}
XCTAssert(n.nodeName == v, "\(n.nodeName) != \(v)")
}
```### Inspect Text Node
```swift
let value = "ABCD"
let docSrc = "\n\(value)\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
guard let children = doc?.documentElement else {
return XCTAssert(false, "No children")
}
XCTAssert(children.nodeName == "a")
do {
let children = children.childNodes
XCTAssert(children.count == 1)
guard let textChild = children.first as? XText else {
return XCTAssert(false)
}
XCTAssert(textChild.nodeValue == value)
}
```### Check Node Type
```swift
let value = "ABCD"
let docSrc = "\n\(value)\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
guard let children = doc?.documentElement else {
return XCTAssert(false, "No children")
}
XCTAssert(children.nodeName == "a")
let nodeType = children.nodeType
if case .elementNode = nodeType {
XCTAssert(true)
} else {
XCTAssert(false, "\(nodeType)")
}
```### First & Last Child
```swift
let docSrc = "\n\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
guard let children = doc?.documentElement else {
return XCTAssert(false, "No children")
}
XCTAssert(children.nodeName == "a")
guard let firstChild = children.firstChild else {
return XCTAssert(false)
}
guard let lastChild = children.lastChild else {
return XCTAssert(false)
}
XCTAssert(firstChild.nodeName == "b")
XCTAssert(lastChild.nodeName == "d")
```### Next & Previous Sibling
```swift
let docSrc = "\n\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
guard let children = doc?.documentElement else {
return XCTAssert(false, "No children")
}
XCTAssert(children.nodeName == "a")
guard let firstChild = children.firstChild else {
return XCTAssert(false)
}
XCTAssert(firstChild.nodeName == "b")
guard let nextSib = firstChild.nextSibling else {
return XCTAssert(false)
}
guard let prevSib = nextSib.previousSibling else {
return XCTAssert(false)
}
XCTAssert(nextSib.nodeName == "c")
XCTAssert(prevSib.nodeName == "b")
```### Element Attributes
```swift
let docSrc = "\n\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
guard let children = doc?.documentElement else {
return XCTAssert(false, "No children")
}
XCTAssert(children.nodeName == "a")
guard let firstChild = children.firstChild as? XElement else {
return XCTAssert(false)
}
XCTAssert(firstChild.nodeName == "b")
guard let atr1 = firstChild.getAttribute(name: "atr1") else {
return XCTAssert(false)
}
XCTAssert(atr1 == "the value")
guard let atr2 = firstChild.getAttributeNode(name: "atr2") else {
return XCTAssert(false)
}
XCTAssert(atr2.value == "the other value")
```With namespaces:
```swift
let names = ["atr1", "atr2"]
let docSrc = "\n\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
guard let children = doc?.documentElement else {
return XCTAssert(false, "No children")
}
XCTAssert(children.nodeName == "a")
guard let firstChild = children.firstChild else {
return XCTAssert(false)
}
XCTAssert(firstChild.nodeName == "b")
guard let attrs = firstChild.attributes else {
return XCTAssert(false, "nil attributes")
}
XCTAssert(attrs.length == 2)
for name in names {
guard let item = attrs.getNamedItemNS(namespaceURI: "foo:bar", localName: name) else {
return XCTAssert(false)
}
XCTAssert(item.nodeName == name)
}
```### Get Elements By Name
```swift
let docSrc = "\n\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
guard let elements = doc?.documentElement?.getElementsByTagName("b") else {
return XCTAssert(false)
}
XCTAssert(elements.count == 3)
for node in elements {
XCTAssert(node.nodeName == "b")
}
```With namespaces:
```swift
let docSrc = "\nFOO\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
do {
guard let elements = doc?.getElementsByTagNameNS(namespaceURI: "foo:bar", localName: "a") else {
return XCTAssert(false)
}
XCTAssert(elements.count == 1)
for node in elements {
XCTAssert(node.nodeName == "a")
XCTAssert(node.localName == "a")
XCTAssert(node.prefix == "foo")
XCTAssert(node.namespaceURI == "foo:bar")
}
}
do {
guard let elements = doc?.documentElement?.getElementsByTagNameNS(namespaceURI: "foo:bar", localName: "a") else {
return XCTAssert(false)
}
XCTAssert(elements.count == 1)
for node in elements {
XCTAssert(node.nodeName == "a")
XCTAssert(node.localName == "a")
XCTAssert(node.prefix == "foo")
XCTAssert(node.namespaceURI == "foo:bar")
}
}
do {
guard let elements = doc?.getElementsByTagNameNS(namespaceURI: "foo:barz", localName: "a") else {
return XCTAssert(false)
}
XCTAssert(elements.count == 0)
}
do {
guard let elements = doc?.documentElement?.getElementsByTagNameNS(namespaceURI: "foo:barz", localName: "a") else {
return XCTAssert(false)
}
XCTAssert(elements.count == 0)
}
```### Get Element By ID
```swift
let docSrc = "\nFOO\n"
let doc = XDocument(fromSource: docSrc)
XCTAssert(doc?.nodeName == "#document")
guard let element = doc?.getElementById("foo") else {
return XCTAssert(false)
}
XCTAssert(element.tagName == "b")
```### XPath
Elements:
```swift
let docSrc = "\nFOO\n"
guard let doc = XDocument(fromSource: docSrc) else {
return XCTAssert(false)
}
XCTAssert(doc.nodeName == "#document")
let pathRes = doc.extract(path: "/a/b")
guard case .nodeSet(let set) = pathRes else {
return XCTAssert(false, "\(pathRes)")
}
for node in set {
guard let b = node as? XElement else {
return XCTAssert(false, "\(node)")
}
XCTAssert(b.tagName == "b")
}
```Attributes:
```swift
let docSrc = "\nFOO\n"
guard let doc = XDocument(fromSource: docSrc) else {
return XCTAssert(false)
}
XCTAssert(doc.nodeName == "#document")
let pathRes = doc.extract(path: "/a/b/@id")
guard case .nodeSet(let set) = pathRes else {
return XCTAssert(false, "\(pathRes)")
}
for node in set {
guard let b = node as? XAttr else {
return XCTAssert(false, "\(node)")
}
XCTAssert(b.name == "id")
XCTAssert(b.value == "foo")
}
```Text:
```swift
let docSrc = "\nFOO\n"
guard let doc = XDocument(fromSource: docSrc) else {
return XCTAssert(false)
}
XCTAssert(doc.nodeName == "#document")
let pathRes = doc.extract(path: "/a/a/b/text()")
guard case .nodeSet(let set) = pathRes else {
return XCTAssert(false, "\(pathRes)")
}
for node in set {
guard let b = node as? XText else {
return XCTAssert(false, "\(node)")
}
guard let nodeValue = b.nodeValue else {
return XCTAssert(false, "\(b)")
}
XCTAssert(nodeValue == "FOO")
}
```Namespaces:
```swift
let docSrc = "\nFOO\n"
guard let doc = XDocument(fromSource: docSrc) else {
return XCTAssert(false)
}
let namespaces = [("f", "foo:bar")]
let pathRes = doc.extract(path: "/a/f:a", namespaces: namespaces)
guard case .nodeSet(let set) = pathRes else {
return XCTAssert(false, "\(pathRes)")
}
for node in set {
guard let e = node as? XElement else {
return XCTAssert(false, "\(node)")
}
XCTAssert(e.tagName == "a")
XCTAssert(e.namespaceURI == "foo:bar")
XCTAssert(e.prefix == "foo")
}
```