{"id":20038026,"url":"https://github.com/perfectlysoft/perfect-xml","last_synced_at":"2026-04-03T08:36:29.291Z","repository":{"id":63920189,"uuid":"63830771","full_name":"PerfectlySoft/Perfect-XML","owner":"PerfectlySoft","description":"XML support for Perfect.","archived":false,"fork":false,"pushed_at":"2018-04-04T01:26:07.000Z","size":61,"stargazers_count":16,"open_issues_count":0,"forks_count":5,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-05-01T22:02:12.822Z","etag":null,"topics":["perfect","server-side-swift","swift","xml"],"latest_commit_sha":null,"homepage":"https://www.perfect.org","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PerfectlySoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-21T02:37:49.000Z","updated_at":"2021-03-07T12:15:08.000Z","dependencies_parsed_at":"2023-01-14T14:00:34.255Z","dependency_job_id":null,"html_url":"https://github.com/PerfectlySoft/Perfect-XML","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-XML","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-XML/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-XML/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-XML/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PerfectlySoft","download_url":"https://codeload.github.com/PerfectlySoft/Perfect-XML/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252451616,"owners_count":21749958,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["perfect","server-side-swift","swift","xml"],"created_at":"2024-11-13T10:24:50.793Z","updated_at":"2026-04-03T08:36:29.244Z","avatar_url":"https://github.com/PerfectlySoft.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Perfect-XML\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"http://perfect.org/get-involved.html\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://perfect.org/assets/github/perfect_github_2_0_0.jpg\" alt=\"Get Involed with Perfect!\" width=\"854\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/PerfectlySoft/Perfect\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/Perfect_GH_button_1_Star.jpg\" alt=\"Star Perfect On Github\" /\u003e\n    \u003c/a\u003e  \n    \u003ca href=\"http://stackoverflow.com/questions/tagged/perfect\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/perfect_gh_button_2_SO.jpg\" alt=\"Stack Overflow\" /\u003e\n    \u003c/a\u003e  \n    \u003ca href=\"https://twitter.com/perfectlysoft\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/Perfect_GH_button_3_twit.jpg\" alt=\"Follow Perfect on Twitter\" /\u003e\n    \u003c/a\u003e  \n    \u003ca href=\"http://perfect.ly\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/Perfect_GH_button_4_slack.jpg\" alt=\"Join the Perfect Slack\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://developer.apple.com/swift/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Swift-4.0-orange.svg?style=flat\" alt=\"Swift 4.0\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://developer.apple.com/swift/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Platforms-OS%20X%20%7C%20Linux%20-lightgray.svg?style=flat\" alt=\"Platforms OS X | Linux\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://perfect.org/licensing.html\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/License-Apache-lightgrey.svg?style=flat\" alt=\"License Apache\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://twitter.com/PerfectlySoft\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Twitter-@PerfectlySoft-blue.svg?style=flat\" alt=\"PerfectlySoft Twitter\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://perfect.ly\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://perfect.ly/badge.svg\" alt=\"Slack Status\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\nXML \u0026amp; HTML parsing support for Perfect\n\nIt currently contains most of the DOM Core level 2 *read-only* APIs and includes XPath support.\n\n## Building\n\nAdd this project as a dependency in your Package.swift file.\n\n```\n.Package(url:\"https://github.com/PerfectlySoft/Perfect-XML.git\", majorVersion: 3)\n```\n\n## macOS Build Notes\n\nIf you receive a compile error that says the following, you need to install and link libxml2\n\n```\nnote: you may be able to install libxml-2.0 using your system-packager:\n\n    brew install libxml2\n\nCompile Swift Module 'PerfectXML' (2 sources)\n\u003cmodule-includes\u003e:1:9: note: in file included from \u003cmodule-includes\u003e:1:\n#import \"libxml2.h\"\n```\n\nTo install and link libxml2 with homebrew, use the following two commands\n\n```\nbrew install libxml2\nbrew link --force libxml2\n```\n\n## Linux Build Notes\n\nEnsure that you have installed libxml2-dev and pkg-config.\n\n```\nsudo apt-get install libxml2-dev pkg-config\n```\n\n## Usage\n\nInstantiate an XDocument object with your XML string\n\n```swift\nimport PerfectXML\nlet document = XDocument(fromSource: xmlString)\n```\n\nInstantiate an HTMLDocument object with your HTML string\n\n```swift\nimport PerfectXML\nlet document = HTMLDocument(fromSource: htmlString)\n```\n\nNow you can get the root node of the document structure by using the documentElement property\n\n```swift\nprint(document.documentElement?.string(pretty: true))\n```\n\nEach node has several important properties\n\n- nodeValue\n- nodeName\n- parentNode\n- childNodes\n\nEach 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.\n\n```swift\nimport PerfectXML\n\nlet serverResponseXML = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?\u003e\\n\" +\n\"\u003cAutodiscover xmlns=\\\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\\\"\u003e\" + \n\"  \u003cResponse\u003e\" + \n\"    \u003cError Time=\\\"14:52:25.4524532\\\" Id=\\\"3280124998\\\"\u003e\" + \n\"      \u003cErrorCode\u003e600\u003c/ErrorCode\u003e\" + \n\"      \u003cMessage\u003eInvalid Request\u003c/Message\u003e\" +\n\"      \u003cDebugData /\u003e\" +\n\"    \u003c/Error\u003e\" + \n\"  \u003c/Response\u003e\" +\n\"\u003c/Autodiscover\u003e\"\n\nlet serverResponseDocument = XDocument(fromSource: serverResponseXML)\nlet serverMessage = serverResponseDocument?.documentElement?.getElementsByTagName(\"Message\").first?.nodeValue\nprint(serverMessage) // prints Optional(\"Invalid Request\")\n```\n\nConstructing model types from XML feeds is easy with getElementsByTagName\n\n```swift\nimport PerfectXML\n\nlet rssXML = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\" ?\u003e\" +\n\"\u003crss version=\\\"2.0\\\"\u003e\" +\n\"  \u003cchannel\u003e\" +\n\"    \u003ctitle\u003eW3Schools Home Page\u003c/title\u003e\" +\n\"    \u003clink\u003ehttp://www.w3schools.com\u003c/link\u003e\" +\n\"    \u003cdescription\u003eFree web building tutorials\u003c/description\u003e\" +\n\"    \u003citem\u003e\" +\n\"      \u003ctitle\u003eRSS Tutorial\u003c/title\u003e\" +\n\"      \u003clink\u003ehttp://www.w3schools.com/xml/xml_rss.asp\u003c/link\u003e\" +\n\"      \u003cdescription\u003eNew RSS tutorial on W3Schools\u003c/description\u003e\" +\n\"    \u003c/item\u003e\" +\n\"    \u003citem\u003e\" +\n\"      \u003ctitle\u003eXML Tutorial\u003c/title\u003e\" +\n\"      \u003clink\u003ehttp://www.w3schools.com/xml\u003c/link\u003e\" +\n\"      \u003cdescription\u003eNew XML tutorial on W3Schools\u003c/description\u003e\" +\n\"    \u003c/item\u003e\" +\n\"  \u003c/channel\u003e\" +\n\"\u003c/rss\u003e\"\n\nlet rssDocument = XDocument(fromSource: rssXML)\nlet feedItems = rssDocument?.documentElement?.getElementsByTagName(\"item\")\nprint(feedItems?.count) // prints 2\nlet items = feedItems?.map({ MyCustomStruct(xmlNode: $0) })\n```\n\nSometimes 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.\n\n```swift\nimport PerfectXML\n\nlet rssXML = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\" ?\u003e\" +\n\"\u003crss version=\\\"2.0\\\"\u003e\" +\n\"  \u003cchannel\u003e\" +\n\"    \u003ctitle\u003eW3Schools Home Page\u003c/title\u003e\" +\n\"    \u003clink\u003ehttp://www.w3schools.com\u003c/link\u003e\" +\n\"    \u003cdescription\u003eFree web building tutorials\u003c/description\u003e\" +\n\"    \u003citem\u003e\" +\n\"      \u003ctitle\u003eRSS Tutorial\u003c/title\u003e\" +\n\"      \u003clink\u003ehttp://www.w3schools.com/xml/xml_rss.asp\u003c/link\u003e\" +\n\"      \u003cdescription\u003eNew RSS tutorial on W3Schools\u003c/description\u003e\" +\n\"    \u003c/item\u003e\" +\n\"    \u003citem\u003e\" +\n\"      \u003ctitle\u003eXML Tutorial\u003c/title\u003e\" +\n\"      \u003clink\u003ehttp://www.w3schools.com/xml\u003c/link\u003e\" +\n\"      \u003cdescription\u003eNew XML tutorial on W3Schools\u003c/description\u003e\" +\n\"    \u003c/item\u003e\" +\n\"  \u003c/channel\u003e\" +\n\"\u003c/rss\u003e\"\n\nlet rssDocument = XDocument(fromSource: rssXML)\nlet channelNode = rssDocument?.documentElement?.getElementsByTagName(\"channel\").first\nlet channelTitle = channelNode?.childNodes.filter({ $0.nodeName == \"title\" }).first?.nodeValue\nlet channelLink = channelNode?.childNodes.filter({ $0.nodeName == \"link\" }).first?.nodeValue\nlet channelDescription = channelNode?.childNodes.filter({ $0.nodeName == \"description\" }).first?.nodeValue\n\nprint(channelTitle) // Optional(\"W3Schools Home Page\")\nprint(channelLink) // Optional(\"http://www.w3schools.com\")\nprint(channelDescription) // Optional(\"Free web building tutorials\")\n```\n\n### Parse XML Source\n\nThis snippet will parse an XML source string and then convert it back into a string.\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb\u003e\u003cc a=\\\"attr1\\\"\u003eHI\u003c/c\u003e\u003cd/\u003e\u003c/b\u003e\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nlet str = doc?.string(pretty: false)\nXCTAssert(str == docSrc, \"\\(str)\")\n```\n\n### Parse HTML Source\n\nThis snippet will parse an HTML source string.\n\n```swift\nlet docSrc = \"\u003chtml\u003e\\n\u003chead\u003e\\n\u003ctitle\u003etitle\u003c/title\u003e\u003c/head\u003e\\n\u003cbody\u003e\\n\u003cdiv\u003ehi\u003c/div\u003e\\n\u003c/body\u003e\\n\u003c/html\u003e\\n\"\nlet doc = HTMLDocument(fromSource: docSrc)\nlet nodeName = doc?.documentElement?.nodeName\nXCTAssert(nodeName == \"html\")\n```\n\n### Inspect Node Names\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb/\u003e\u003cc/\u003e\u003cd/\u003e\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\nguard let children = doc?.documentElement else {\n\treturn XCTAssert(false, \"No children\")\n}\nXCTAssert(children.nodeName == \"a\")\nlet names = [\"b\", \"c\", \"d\"]\nfor (n, v) in zip(children.childNodes, names) {\n\tguard let _ = n as? XElement else {\n\t\treturn XCTAssert(false)\n\t}\n\tXCTAssert(n.nodeName == v, \"\\(n.nodeName) != \\(v)\")\n}\n```\n\n### Inspect Text Node\n\n```swift\nlet value = \"ABCD\"\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\\(value)\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\nguard let children = doc?.documentElement else {\n\treturn XCTAssert(false, \"No children\")\n}\nXCTAssert(children.nodeName == \"a\")\ndo {\n\tlet children = children.childNodes\n\tXCTAssert(children.count == 1)\n\tguard let textChild = children.first as? XText else {\n\t\treturn XCTAssert(false)\n\t}\n\tXCTAssert(textChild.nodeValue == value)\n}\n```\n\n### Check Node Type\n\n```swift\nlet value = \"ABCD\"\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\\(value)\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\nguard let children = doc?.documentElement else {\n\treturn XCTAssert(false, \"No children\")\n}\nXCTAssert(children.nodeName == \"a\")\nlet nodeType = children.nodeType\nif case .elementNode = nodeType {\n\tXCTAssert(true)\n} else {\n\tXCTAssert(false, \"\\(nodeType)\")\n}\n```\n\n### First \u0026amp; Last Child\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb/\u003e\u003cc/\u003e\u003cd/\u003e\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\nguard let children = doc?.documentElement else {\n\treturn XCTAssert(false, \"No children\")\n}\nXCTAssert(children.nodeName == \"a\")\nguard let firstChild = children.firstChild else {\n\treturn XCTAssert(false)\n}\nguard let lastChild = children.lastChild else {\n\treturn XCTAssert(false)\n}\nXCTAssert(firstChild.nodeName == \"b\")\nXCTAssert(lastChild.nodeName == \"d\")\n```\n\n### Next \u0026amp; Previous Sibling\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb/\u003e\u003cc/\u003e\u003cd/\u003e\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\nguard let children = doc?.documentElement else {\n\treturn XCTAssert(false, \"No children\")\n}\nXCTAssert(children.nodeName == \"a\")\nguard let firstChild = children.firstChild else {\n\treturn XCTAssert(false)\n}\nXCTAssert(firstChild.nodeName == \"b\")\nguard let nextSib = firstChild.nextSibling else {\n\treturn XCTAssert(false)\n}\nguard let prevSib = nextSib.previousSibling else {\n\treturn XCTAssert(false)\n}\nXCTAssert(nextSib.nodeName == \"c\")\nXCTAssert(prevSib.nodeName == \"b\")\n```\n\n### Element Attributes\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb atr1=\\\"the value\\\" atr2=\\\"the other value\\\"\u003e\u003c/b\u003e\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\nguard let children = doc?.documentElement else {\n\treturn XCTAssert(false, \"No children\")\n}\nXCTAssert(children.nodeName == \"a\")\nguard let firstChild = children.firstChild as? XElement else {\n\treturn XCTAssert(false)\n}\nXCTAssert(firstChild.nodeName == \"b\")\nguard let atr1 = firstChild.getAttribute(name: \"atr1\") else {\n\treturn XCTAssert(false)\n}\nXCTAssert(atr1 == \"the value\")\nguard let atr2 = firstChild.getAttributeNode(name: \"atr2\") else {\n\treturn XCTAssert(false)\n}\nXCTAssert(atr2.value == \"the other value\")\n```\n\nWith namespaces:\n\n```swift\nlet names = [\"atr1\", \"atr2\"]\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca xmlns:foo=\\\"foo:bar\\\"\u003e\u003cb foo:atr1=\\\"the value\\\" foo:atr2=\\\"the other value\\\"\u003e\u003c/b\u003e\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\nguard let children = doc?.documentElement else {\n\treturn XCTAssert(false, \"No children\")\n}\nXCTAssert(children.nodeName == \"a\")\nguard let firstChild = children.firstChild else {\n\treturn XCTAssert(false)\n}\nXCTAssert(firstChild.nodeName == \"b\")\nguard let attrs = firstChild.attributes else {\n\treturn XCTAssert(false, \"nil attributes\")\n}\nXCTAssert(attrs.length == 2)\nfor name in names {\n\tguard let item = attrs.getNamedItemNS(namespaceURI: \"foo:bar\", localName: name) else {\n\t\treturn XCTAssert(false)\n\t}\n\tXCTAssert(item.nodeName == name)\n}\n```\n\n### Get Elements By Name\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb/\u003e\u003ca\u003e\u003cb\u003e\u003cb/\u003e\u003c/b\u003e\u003c/a\u003e\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\nguard let elements = doc?.documentElement?.getElementsByTagName(\"b\") else {\n\treturn XCTAssert(false)\n}\nXCTAssert(elements.count == 3)\nfor node in elements {\n\tXCTAssert(node.nodeName == \"b\")\n}\n```\n\nWith namespaces:\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca xmlns:foo=\\\"foo:bar\\\"\u003e\u003cb/\u003e\u003cfoo:a\u003e\u003cb\u003eFOO\u003cb/\u003e\u003c/b\u003e\u003c/foo:a\u003e\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\ndo {\n\tguard let elements = doc?.getElementsByTagNameNS(namespaceURI: \"foo:bar\", localName: \"a\") else {\n\t\treturn XCTAssert(false)\n\t}\n\tXCTAssert(elements.count == 1)\n\tfor node in elements {\n\t\tXCTAssert(node.nodeName == \"a\")\n\t\tXCTAssert(node.localName == \"a\")\n\t\tXCTAssert(node.prefix == \"foo\")\n\t\tXCTAssert(node.namespaceURI == \"foo:bar\")\n\t}\n}\ndo {\n\tguard let elements = doc?.documentElement?.getElementsByTagNameNS(namespaceURI: \"foo:bar\", localName: \"a\") else {\n\t\treturn XCTAssert(false)\n\t}\n\tXCTAssert(elements.count == 1)\n\tfor node in elements {\n\t\tXCTAssert(node.nodeName == \"a\")\n\t\tXCTAssert(node.localName == \"a\")\n\t\tXCTAssert(node.prefix == \"foo\")\n\t\tXCTAssert(node.namespaceURI == \"foo:bar\")\n\t}\n}\ndo {\n\tguard let elements = doc?.getElementsByTagNameNS(namespaceURI: \"foo:barz\", localName: \"a\") else {\n\t\treturn XCTAssert(false)\n\t}\n\tXCTAssert(elements.count == 0)\n}\ndo {\n\tguard let elements = doc?.documentElement?.getElementsByTagNameNS(namespaceURI: \"foo:barz\", localName: \"a\") else {\n\t\treturn XCTAssert(false)\n\t}\n\tXCTAssert(elements.count == 0)\n}\n```\n\n### Get Element By ID\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb id=\\\"foo\\\"/\u003e\u003ca\u003e\u003cb\u003eFOO\u003cb/\u003e\u003c/b\u003e\u003c/a\u003e\u003c/a\u003e\\n\"\nlet doc = XDocument(fromSource: docSrc)\nXCTAssert(doc?.nodeName == \"#document\")\nguard let element = doc?.getElementById(\"foo\") else {\n\treturn XCTAssert(false)\n}\nXCTAssert(element.tagName == \"b\")\n```\n\n### XPath\n\nElements:\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb id=\\\"foo\\\"/\u003e\u003ca\u003e\u003cb\u003eFOO\u003cb/\u003e\u003c/b\u003e\u003c/a\u003e\u003c/a\u003e\\n\"\nguard let doc = XDocument(fromSource: docSrc) else {\n\treturn XCTAssert(false)\n}\nXCTAssert(doc.nodeName == \"#document\")\nlet pathRes = doc.extract(path: \"/a/b\")\nguard case .nodeSet(let set) = pathRes else {\n\treturn XCTAssert(false, \"\\(pathRes)\")\n}\nfor node in set {\n\tguard let b = node as? XElement else {\n\t\treturn XCTAssert(false, \"\\(node)\")\n\t}\n\tXCTAssert(b.tagName == \"b\")\n}\n```\n\nAttributes:\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb id=\\\"foo\\\"/\u003e\u003ca\u003e\u003cb\u003eFOO\u003cb/\u003e\u003c/b\u003e\u003c/a\u003e\u003c/a\u003e\\n\"\nguard let doc = XDocument(fromSource: docSrc) else {\n\treturn XCTAssert(false)\n}\nXCTAssert(doc.nodeName == \"#document\")\nlet pathRes = doc.extract(path: \"/a/b/@id\")\nguard case .nodeSet(let set) = pathRes else {\n\treturn XCTAssert(false, \"\\(pathRes)\")\n}\nfor node in set {\n\tguard let b = node as? XAttr else {\n\t\treturn XCTAssert(false, \"\\(node)\")\n\t}\n\tXCTAssert(b.name == \"id\")\n\tXCTAssert(b.value == \"foo\")\n}\n```\n\nText:\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca\u003e\u003cb id=\\\"foo\\\"/\u003e\u003ca\u003e\u003cb\u003eFOO\u003cb/\u003e\u003c/b\u003e\u003c/a\u003e\u003c/a\u003e\\n\"\nguard let doc = XDocument(fromSource: docSrc) else {\n\treturn XCTAssert(false)\n}\nXCTAssert(doc.nodeName == \"#document\")\nlet pathRes = doc.extract(path: \"/a/a/b/text()\")\nguard case .nodeSet(let set) = pathRes else {\n\treturn XCTAssert(false, \"\\(pathRes)\")\n}\nfor node in set {\n\tguard let b = node as? XText else {\n\t\treturn XCTAssert(false, \"\\(node)\")\n\t}\n\tguard let nodeValue = b.nodeValue else {\n\t\treturn XCTAssert(false, \"\\(b)\")\n\t}\n\tXCTAssert(nodeValue == \"FOO\")\n}\n```\n\nNamespaces:\n\n```swift\nlet docSrc = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\\n\u003ca xmlns:foo=\\\"foo:bar\\\"\u003e\u003cb/\u003e\u003cfoo:a\u003e\u003cb\u003eFOO\u003cb/\u003e\u003c/b\u003e\u003c/foo:a\u003e\u003c/a\u003e\\n\"\nguard let doc = XDocument(fromSource: docSrc) else {\n\treturn XCTAssert(false)\n}\nlet namespaces = [(\"f\", \"foo:bar\")]\nlet pathRes = doc.extract(path: \"/a/f:a\", namespaces: namespaces)\nguard case .nodeSet(let set) = pathRes else {\n\treturn XCTAssert(false, \"\\(pathRes)\")\n}\nfor node in set {\n\tguard let e = node as? XElement else {\n\t\treturn XCTAssert(false, \"\\(node)\")\n\t}\n\tXCTAssert(e.tagName == \"a\")\n\tXCTAssert(e.namespaceURI == \"foo:bar\")\n\tXCTAssert(e.prefix == \"foo\")\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfectlysoft%2Fperfect-xml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fperfectlysoft%2Fperfect-xml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfectlysoft%2Fperfect-xml/lists"}