{"id":22385503,"url":"https://github.com/sersoft-gmbh/xmlwrangler","last_synced_at":"2025-07-31T04:33:23.731Z","repository":{"id":37706060,"uuid":"93170473","full_name":"sersoft-gmbh/xmlwrangler","owner":"sersoft-gmbh","description":"Easily deal with XMLs in Swift","archived":false,"fork":false,"pushed_at":"2024-09-18T12:41:10.000Z","size":3237,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-11-20T22:04:26.972Z","etag":null,"topics":["parse-xmls","swift","xml"],"latest_commit_sha":null,"homepage":"https://sersoft-gmbh.github.io/xmlwrangler/","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/sersoft-gmbh.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":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-02T13:54:59.000Z","updated_at":"2024-09-18T07:04:10.000Z","dependencies_parsed_at":"2024-08-26T09:40:46.702Z","dependency_job_id":"ba7b97fe-8dfc-494f-91ed-92b4ed8b657f","html_url":"https://github.com/sersoft-gmbh/xmlwrangler","commit_stats":{"total_commits":205,"total_committers":2,"mean_commits":102.5,"dds":"0.42439024390243907","last_synced_commit":"84e885f27a84433dd8be8603b785fe919bbb5923"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sersoft-gmbh%2Fxmlwrangler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sersoft-gmbh%2Fxmlwrangler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sersoft-gmbh%2Fxmlwrangler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sersoft-gmbh%2Fxmlwrangler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sersoft-gmbh","download_url":"https://codeload.github.com/sersoft-gmbh/xmlwrangler/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228217212,"owners_count":17886701,"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":["parse-xmls","swift","xml"],"created_at":"2024-12-05T01:25:59.079Z","updated_at":"2024-12-05T01:25:59.800Z","avatar_url":"https://github.com/sersoft-gmbh.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# XMLWrangler\n\n[![GitHub release](https://img.shields.io/github/release/sersoft-gmbh/xmlwrangler.svg?style=flat)](https://github.com/sersoft-gmbh/xmlwrangler/releases/latest)\n![Tests](https://github.com/sersoft-gmbh/xmlwrangler/workflows/Tests/badge.svg)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/8932a462fa6d4cd6b2850069c68195c0)](https://www.codacy.com/gh/sersoft-gmbh/xmlwrangler/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=sersoft-gmbh/xmlwrangler\u0026amp;utm_campaign=Badge_Grade)\n[![codecov](https://codecov.io/gh/sersoft-gmbh/xmlwrangler/branch/master/graph/badge.svg?token=zTgWMfSyCb)](https://codecov.io/gh/sersoft-gmbh/xmlwrangler)\n[![Docs](https://img.shields.io/badge/-documentation-informational)](https://sersoft-gmbh.github.io/xmlwrangler)\n\nEasily deal with XMLs in Swift.\n\n## Installation\n\nAdd the following dependency to your `Package.swift`:\n```swift\n.package(url: \"https://github.com/sersoft-gmbh/xmlwrangler\", from: \"6.0.0\"),\n```\n\n## Compatibility\n\n| **Swift**          | **XMLWrangler Package**  |\n|--------------------|--------------------------|\n| \u003c  5.2.0           | 1.x.y - 3.x.y            |\n| \u003e= 5.3.0, \u003c 5.9.0  | 5.x.y                    |\n| \u003e= 5.9.0           | 6.x.y                    |\n\n## Usage\n\n### XMLElement\n\nEvery element in an XML is represented by the `XMLElement` struct. It has three properties, `name` which reflects the element's tag name, `attributes` which contains all attributes of the element and `content` which describes the content of the element.\nThe content is an collection whose `Element` is an enum. The enum has two cases: `.string` and `.element`. The order in the collection is the order in which the content has been found. So if an element first contains some text, then contains a child element and finally again some text,  `content` will contain a `.string` whose associated `StringPart` is the first text. Next there would be a `.element` whose associated `XMLElement` would be the child element. Finally, there would be another `.string` with the last text.\n\nWhile you can create an `XMLElement` with a content of `[.string(\"abc\"), .string(\"def\"), .element(XMLElement(name: \"test\"))]`, and it would also lead to valid XML, it could be cleaned up to `[.string(\"abcdef\"), .element(XMLElement(name: \"test\"))]`. To achieve that, it's recommended to use the various `append` functions on `XMLElement.content` or even `XMLElement` directly when you can't assure that the content is cleaned upon creation. If your element was created with an empty content (`[]`), and you append each of the content elements above, the `append` functions make sure that they append the \"def\" string to the first \"abc\" string instead of adding another `.string` to the content. If for some reason you still end up with a situation where your content has consecutive `.string` elements, there's a convenience function `compress()` (or it's non-mutating sibling `compressed()`), which merges these `.string` elements into one.\n\nAn `XMLElement` can be compared to another element and is considered equal if all three properties (`name`, `attributes` and `content`) are equal. This means that for a big tree, all children of the root element will be compared. So be careful when comparing big trees and fall back to manually comparing `name` and/or `attributes` if necessary. `XMLElement` also conforms to `Identifiable` and uses the `name` as `id`.\n\nBoth, serializing and parsing XMLs with XMLWrangler relies on `XMLElement`.\n\n### Parsing XMLs\n\nParsing existing XMLs can be done using the static functions on `XMLElement`. You can parse either a given `Data` object or a `String` containing the XML.\nIf parsing succeeds, the parsed root object is returned. Otherwise whatever error happend along the way is thrown. Errors thrown are the ones created by `Foundation.XMLParser`.\n\n```swift\ndo {\n    let xml = \"\"\"\n              \u003c?xml version='1.0' encoding='UTF-8'?\u003e\n              \u003croot myattr='myvalue'\u003e\n                  \u003cchild1/\u003e\n                  \u003cchild2\u003esome text\u003c/child2\u003e\n              \u003c/root\u003e\n              \"\"\"\n    let root = try XMLElement.parse(xml)\n} catch {\n    print(\"Something went wrong while parsing: \\(error)\")\n}\n```\n\nIn this example, `root.name.rawValue` would of course be `\"root\"`. `root.content` would contain two `.element`s. The first would have a associated `XMLElement` with a `name` of `\"child1\"` and an empty `content`. The `name` of `XMLElement` of the second `.element` would be `\"child2\"` and its content would contain one `.string` having `\"some text\"` associated. `root.attributes` would contain the value `\"myvalue\"` for the key `\"myattr\"`.\n\n### Serializing XMLElements\n\nSince you can parse XMLs, you can also convert an `XMLElement` to a String. For this, there are two functions on `XMLElement`.\nThe first one just converts an `XMLElement` into a `String`. This happens by creating an opening and ending tag (where the beginning tag contains the `attributes` if available) and putting the `content` of the element in between. Also, `content` is compressed (using the aforementioned `compress` function) before being serialized.\n\n```swift\nvar root = XMLElement(name: \"root\", attributes: [\"myattr\": \"myvalue\"])\nroot.content.append(element: \"child1\")\nroot.content.append(element: XMLElement(name: \"child2\", content: \"some text\"))\n\nlet xmlString = xml.serialize() // -\u003e \"\u003croot myattr=\\\"myvalue\\\"\u003e\u003cchild1/\u003e\u003cchild2\u003esome text\u003c/child2\u003e\u003c/root\u003e\"\n```\n\nIf the traditional XML header should also be added, there's a second function which takes a version and a document encoding as additional parameters, but otherwise follows the same rules:\n\n```swift\nvar root = XMLElement(name: \"root\", attributes: [\"myattr\": \"myvalue\"])\nroot.content.append(element: \"child1\")\nroot.content.append(element: XMLElement(name: \"child2\", content: \"some text\"))\n\nlet xmlDocumentString = root.serializeAsDocument(at: DocumentVersion(major: 1), using: .utf8)\n// -\u003e \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\u003croot myattr=\\\"myvalue\\\"\u003e\u003cchild1/\u003e\u003cchild2\u003esome text\u003c/child2\u003e\u003c/root\u003e\"\n```\n\nPlease note that XMLWrangler does not escape the string based on the given encoding. It simply uses it the generate the document header.\n\nBoth functions can take an additional parameter `options` which contains a set of options to control the serialization behaviour. Currently the following options are possible:\n\n-   `.pretty`: Use pretty formatting. This adds newlines around the tags to make the resulting XML more readable. This is usually not needed for processing XML.\n-   `.singleQuoteAttributes`: When this option is present, then attributes of elements will be enclosed in single quotes (') instead of double quotes (\").\n-   `.explicitClosingTag`: This option forces empty elements to be serialized with an explicit closing tag instead of using the shorthand `/\u003e` syntax.\n\n### Type safety\n\nXMLWrangler will always extract all content and attributes as `String` internally. This is because XML itself does not differentiate between types like e.g. JSON does.\nHowever, there are many helper functions to safely look up and convert content and attributes of an `XMLElement`:\n\n-   First, there are helpers to extract all child elements with a given name: `XMLElement.elements(named:)`\n-   Next, there are helpers to extract an element at a given path: `XMLElement.element(at:)`\n-   Another helper allows to extract attributes of an element: `XMLElement.attribute(for:)`.\n-   It is then also possible to convert those attributes (for some types like e.g. `RawRepresentable` you don't need to pass a `converter`): `XMLElement.convertedAttribute(for:converter:)`\n-   Last but not least you can extract the string content of an Element: `XMLElement.stringContent()`\n-   And of course as you can with attributes, you can also convert string content: `XMLElement.convertedStringContent(converter:)`\n\nAll these methods throw an error (`XMLElement.LookupError`) when something went wrong instead of returning optionals. If you prefern an optional, you can always use `try?`.\nFor more information also check the header docs which describe these methods a little closer.\n\n## Possible Features\n\nWhile not yet integrated, the following features might provide added value and could make it into XMLWrangler in the future:\n\n-   Indention support for serializing and parsing.\n-   Extracting \"KeyPaths\": It could be useful to directly extract a path. It would not be necessary to extract every single element then.\n\n## Documentation\n\nThe API is documented using header doc. If you prefer to view the documentation as a webpage, there is an [online version](https://sersoft-gmbh.github.io/xmlwrangler) available for you.\n\n## Contributing\n\nIf you find a bug / like to see a new feature in XMLWrangler there are a few ways of helping out:\n\n-   If you can fix the bug / implement the feature yourself please do and open a PR.\n-   If you know how to code (which you probably do), please add a (failing) test and open a PR. We'll try to get your test green ASAP.\n-   If you can do neither, then open an issue. While this might be the easiest way, it will likely take the longest for the bug to be fixed / feature to be implemented.\n\n## License \u0026 Copyright\n\nSee [LICENSE](./LICENSE) file.\n\nCopyright \u0026copy; 2016-2023 ser.soft GmbH.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsersoft-gmbh%2Fxmlwrangler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsersoft-gmbh%2Fxmlwrangler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsersoft-gmbh%2Fxmlwrangler/lists"}