{"id":13395745,"url":"https://github.com/renggli/dart-xml","last_synced_at":"2025-05-14T15:00:37.409Z","repository":{"id":16237912,"uuid":"18985556","full_name":"renggli/dart-xml","owner":"renggli","description":"Lightweight library for parsing, traversing, and transforming XML in Dart.","archived":false,"fork":false,"pushed_at":"2025-01-19T09:24:44.000Z","size":1288,"stargazers_count":226,"open_issues_count":3,"forks_count":51,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-31T15:09:46.927Z","etag":null,"topics":["dart","flutter","sax","xml","xml-builder","xml-document","xml-parser","xml-parsing","xml-queries","xml-transformation","xpath"],"latest_commit_sha":null,"homepage":"http://pub.dartlang.org/packages/xml","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/renggli.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-04-21T07:28:30.000Z","updated_at":"2025-03-24T18:42:26.000Z","dependencies_parsed_at":"2024-03-16T22:59:38.314Z","dependency_job_id":"67fd5683-98de-491a-bed7-9da59a7866e9","html_url":"https://github.com/renggli/dart-xml","commit_stats":{"total_commits":756,"total_committers":20,"mean_commits":37.8,"dds":"0.060846560846560815","last_synced_commit":"ac5cd8c235f3dfd54b44b4af4513b2dbdb6c6a87"},"previous_names":[],"tags_count":141,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renggli%2Fdart-xml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renggli%2Fdart-xml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renggli%2Fdart-xml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renggli%2Fdart-xml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/renggli","download_url":"https://codeload.github.com/renggli/dart-xml/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247182427,"owners_count":20897381,"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":["dart","flutter","sax","xml","xml-builder","xml-document","xml-parser","xml-parsing","xml-queries","xml-transformation","xpath"],"created_at":"2024-07-30T18:00:30.549Z","updated_at":"2025-04-04T14:02:05.195Z","avatar_url":"https://github.com/renggli.png","language":"Dart","funding_links":[],"categories":["Dart","Libraries"],"sub_categories":["Parsers"],"readme":"Dart XML\n========\n\n[![Pub Package](https://img.shields.io/pub/v/xml.svg)](https://pub.dev/packages/xml)\n[![Build Status](https://github.com/renggli/dart-xml/actions/workflows/dart.yml/badge.svg?branch=main)](https://github.com/renggli/dart-xml/actions/workflows/dart.yml)\n[![Code Coverage](https://codecov.io/gh/renggli/dart-xml/branch/main/graph/badge.svg?token=TDwmzZtPdj)](https://codecov.io/gh/renggli/dart-xml)\n[![GitHub Issues](https://img.shields.io/github/issues/renggli/dart-xml.svg)](https://github.com/renggli/dart-xml/issues)\n[![GitHub Forks](https://img.shields.io/github/forks/renggli/dart-xml.svg)](https://github.com/renggli/dart-xml/network)\n[![GitHub Stars](https://img.shields.io/github/stars/renggli/dart-xml.svg)](https://github.com/renggli/dart-xml/stargazers)\n[![GitHub License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/renggli/dart-xml/main/LICENSE)\n\nDart XML is a lightweight library for parsing, traversing, querying, transforming and building XML documents.\n\nThis library provides a [DOM-based](#Reading-and-Writing) object model for accessing and manipulating XML documents, as\nwell as an [event-based](#Event-driven) (comparable to SAX) for incremental reading and processing of XML streams.\nFurthermore, it supports a large subset of [XPath](#XPath) to simplify the querying of large documents.\n\nThis library is open source, stable and well tested. Development happens\non [GitHub](https://github.com/renggli/dart-xml). Feel free to report issues or create a pull-request there. General\nquestions are best asked on [StackOverflow](https://stackoverflow.com/questions/tagged/xml+dart).\n\nThe package is hosted on [dart packages](https://pub.dev/packages/xml).\nUp-to-date [class documentation](https://pub.dev/documentation/xml/latest/) is created with every release.\n\n\nTutorial\n--------\n\n### Installation\n\nFollow the installation instructions on [dart packages](https://pub.dev/packages/xml/install).\n\nImport the library into your Dart code using:\n\n```dart\nimport 'package:xml/xml.dart';\n```\n\n:warning: This library makes extensive use of [static extension methods](https://dart.dev/guides/language/extension-methods). If you [import the library](https://dart.dev/guides/language/language-tour#using-libraries) using a _library prefix_ or only _selectively show classes_ you might miss some of its functionality. For historical reasons public classes have an `Xml` prefix, so conflicts with other code should be rare.\n\n### Reading and Writing\n\nTo read XML input use the factory method `XmlDocument.parse(String input)`:\n\n```dart\nfinal bookshelfXml = '''\u003c?xml version=\"1.0\"?\u003e\n    \u003cbookshelf\u003e\n      \u003cbook\u003e\n        \u003ctitle lang=\"en\"\u003eGrowing a Language\u003c/title\u003e\n        \u003cprice\u003e29.99\u003c/price\u003e\n      \u003c/book\u003e\n      \u003cbook\u003e\n        \u003ctitle lang=\"en\"\u003eLearning XML\u003c/title\u003e\n        \u003cprice\u003e39.95\u003c/price\u003e\n      \u003c/book\u003e\n      \u003cprice\u003e132.00\u003c/price\u003e\n    \u003c/bookshelf\u003e''';\nfinal document = XmlDocument.parse(bookshelfXml);\n```\n\nThe resulting object is an instance of `XmlDocument`. In case the document cannot be parsed, a `XmlException` is thrown.\n\nTo write back the parsed XML document, simply call `toString()` or `toXmlString(...)` if you need more control:\n\n```dart\nprint(document.toString());\nprint(document.toXmlString(pretty: true, indent: '\\t'));\n```\n\nTo read XML from a file use the [dart:io](https://api.dart.dev/dart-io/dart-io-library.html) library:\n\n```dart\nfinal file = new File('bookshelf.xml');\nfinal document = XmlDocument.parse(file.readAsStringSync());\n```\n\nIf your file is not _UTF-8_ encoded pass the correct encoding to `readAsStringSync`. It is the responsibility of the caller to provide a standard Dart [String] using the default UTF-16 encoding. To read and write large files you might want to use the [event-driven API](#event-driven) instead.\n\n### Traversing and Querying\n\nAccessors allow accessing nodes in the XML tree:\n\n- `attributes` returns the attributes of the node.\n- `children` returns the direct children of the node.\n\nBoth lists are mutable and support all common `List` methods, such as `add(XmlNode)`, `addAll(Iterable\u003cXmlNode\u003e)`, `insert(int, XmlNode)`, and `insertAll(int, Iterable\u003cXmlNode\u003e)`. Trying to add a `null` value or an unsupported node type throws an `XmlNodeTypeError` error. Nodes that are already part of a tree _are not_ automatically moved, you need to first create a copy as otherwise an `XmlParentError` is thrown. `XmlDocumentFragment` nodes are automatically expanded and copies of their children are added.\n\nThere are methods to traverse the XML tree along different axes:\n\n- `siblings` returns an iterable over the nodes at the same level that proceed and follow this node in document order.\n- `preceding` returns an iterable over nodes preceding the opening tag of the current node in document order.\n- `descendants` returns an iterable over the descendants of the current node in document order. This includes the attributes of the current node, its children, the grandchildren, and so on.\n- `following` the nodes following the closing tag of the current node in document order.\n- `ancestors` returns an iterable over the ancestor nodes of the current node, that is the parent, the grandparent, and so on. Note that this is the only iterable that traverses nodes in reverse document order.\n\nFor example, the `descendants` iterator could be used to extract all textual contents from an XML tree:\n\n```dart\nfinal textual = document.descendants\n    .whereType\u003cXmlText\u003e()\n    .map((text) =\u003e text.value.trim())\n    .where((string) =\u003e string.isNotEmpty)\n    .join('\\n');\nprint(textual);   // prints 'Growing a Language', '29.99', 'Learning XML', '39.95', and '132.00'\n```\n\nThere are convenience helpers to filter by element nodes only: `childElements`, `siblingElements`, `precedingElements`, `descendantElements`, `followingElements`, and `ancestorElements`.\n\nAdditionally, there are helpers to find elements with a specific tag:\n\n- `getElement(String name)` finds the first direct child with the provided tag `name`, or `null`.\n- `findElements(String name)` finds direct children of the current node with the provided tag `name`.\n- `findAllElements(String name)` finds direct and indirect children of the current node with the provided tag `name`.\n\nFor example, to find all the nodes with the _\u0026lt;title\u0026gt;_ tag you could write:\n\n```dart\nfinal titles = document.findAllElements('title');\n```\n\nThe above code returns a lazy iterator that recursively walks the XML document and yields all the element nodes with the requested tag name. To extract the textual contents of an element call `innerText`:\n\n```dart\ntitles\n    .map((element) =\u003e element.innerText)\n    .forEach(print);   // prints 'Growing a Language' and 'Learning XML'\n```\n\nThis prints _Growing a Language_ and _Learning XML_.\n\nSimilarly, to compute the total price of all the books one could write the following expression:\n\n```dart\nfinal total = document.findAllElements('book')\n    .map((element) =\u003e double.parse(element\n        .findElements('price')\n        .single\n        .innerText))\n    .reduce((a, b) =\u003e a + b);\nprint(total);   // prints 69.94\n```\n\nNote that this first finds all the books, and then extracts the price to avoid counting the price tag that is included\nin the bookshelf.\n\n#### XPath\n\nTo simplify accessing and extracting specific parts of a DOM document, this library supports the most commonly used\nsubset of [XPath 1.0](https://www.w3.org/TR/1999/REC-xpath-19991116/) expressions; a full XPath engine is outside the\nscope of this library.\n\nTo get started import the XPath library:\n\n```dart\nimport 'package:xml/xpath.dart';\n```\n\nThis exposes the static extension method `XmlNode.xpath(String expression)` that can be used on documents, and any other\nXML DOM node. The method returns an iterable over the matching XML DOM nodes. Using the `bookshelf` data defined above\nwe can write:\n\n```dart\n// Find all the books in the bookshelf.\nprint(document.xpath('/bookshelf/book'));\n\n// Find the second book in the bookshelf.\nprint(document.xpath('/bookshelf/book[2]'));\n\n// Find all the english titles anywhere in the document.\nprint(document.xpath('//title[@lang=\"en\"]'));\n\n// Find all the books with an english title.\nprint(document.xpath('//book[title/@lang=\"en\"]'));\n\n// Sum up the prices of all the books.\nfinal total = document.xpath('//book/price/text()')\n        .map((node) =\u003e double.parse(node.value!))\n        .reduce((a, b) =\u003e a + b);\nprint(total);   // prints 69.94\n```\n\n### Building\n\nWhile it is possible to instantiate and compose `XmlDocument`, `XmlElement` and `XmlText` nodes manually,\nthe `XmlBuilder` provides a simple fluent API to build complete XML trees. To create the above bookshelf example one\nwould write:\n\n```dart\nfinal builder = XmlBuilder();\nbuilder.processing('xml', 'version=\"1.0\"');\nbuilder.element('bookshelf', nest: () {\n  builder.element('book', nest: () {\n    builder.element('title', nest: () {\n      builder.attribute('lang', 'en');\n      builder.text('Growing a Language');\n    });\n    builder.element('price', nest: 29.99);\n  });\n  builder.element('book', nest: () {\n    builder.element('title', nest: () {\n      builder.attribute('lang', 'en');\n      builder.text('Learning XML');\n    });\n    builder.element('price', nest: 39.95);\n  });\n  builder.element('price', nest: '132.00');\n});\nfinal document = builder.buildDocument();\n```\n\nThe `element` method supports optional named arguments:\n\n- The most common is the `nest:` argument which is used to insert contents into the element. In most cases this will be a function that calls more methods on the builder to define attributes, declare namespaces and add child elements. However, the argument can also be a string or an arbitrary Dart object that is converted to a string and added as a text node.\n- While attributes can be defined from within the element, for simplicity there is also an argument `attributes:` that takes a map to define simple name-value pairs.\n- Furthermore, we can provide a URI as the namespace of the element using `namespace:` and declare new namespace prefixes using `namespaces:`. For details see the documentation of the method.\n\nThe builder pattern allows you to easily extract repeated parts into specific methods. In the example above, one could put the part writing a book into a separate method as follows:\n\n```dart\nvoid buildBook(XmlBuilder builder, String title, String language, num price) {\n  builder.element('book', nest: () {\n    builder.element('title', nest: () {\n      builder.attribute('lang', language);\n      builder.text(title);\n    });\n    builder.element('price', nest: price);\n  });\n}\n```\n\nThe above `buildDocument()` method returns the built document. To attach built nodes into an existing XML document, use `buildFragment()`. Once the builder returns the built node, its internal state is reset.\n\n```dart\nfinal builder = XmlBuilder();\nbuildBook(builder, 'The War of the Worlds', 'en', 12.50);\nbuildBook(builder, 'Voyages extraordinaries', 'fr', 18.20);\ndocument.rootElement.children.add(builder.buildFragment());\n```\n\n### Event-driven\n\nReading large XML files and instantiating their DOM into the memory can be expensive. As an alternative this library provides the possibility to read and transform XML documents as a sequence of events using Dart Iterables or [Streams](https://dart.dev/tutorials/language/streams). These approaches are comparable to event-driven SAX parsing known from other libraries.\n\n```dart\nimport 'package:xml/xml_events.dart';\n```\n\n#### Iterables\n\nIn the simplest case you can get a `Iterable\u003cXmlEvent\u003e` over the input string using the following code. This parses the input lazily, and only parses input when requested:\n\n```dart\nparseEvents(bookshelfXml)\n    .whereType\u003cXmlTextEvent\u003e()\n    .map((event) =\u003e event.value.trim())\n    .where((text) =\u003e text.isNotEmpty)\n    .forEach(print);\n```\n\nThe function `parseEvents` supports various other options, see [its documentation](https://pub.dev/documentation/xml/latest/xml_events/parseEvents.html) for further examples.\n\nThis approach requires the whole input to be available at the beginning and does not work if the data itself is only available asynchronous, such as coming from a slow network connection. A more flexible, but also more complicated API is provided with [Dart Streams](https://dart.dev/tutorials/language/streams).\n\n#### Streams\n\nTo asynchronously parse and process events directly from a file or HTTP stream use the provided extension methods on `Stream` to convert between streams of strings, events and DOM tree nodes:\n\n![Stream Extensions Methods](https://raw.githubusercontent.com/renggli/dart-xml/HEAD/doc/stream-ext.png)\n\nFor more control the underlying `Codec` and `Converter` implementations can be used:\n\n![Stream Codec and Converter](https://raw.githubusercontent.com/renggli/dart-xml/HEAD/doc/stream-codec.png)\n\nVarious other transformations are provided to simplify processing complex streams:\n\n- Normalizes a sequence of `XmlEvent` objects by removing empty and combining adjacent text events. \\\n  `Stream\u003cList\u003cXmlEvent\u003e\u003e normalizeEvents()` on `Stream\u003cList\u003cXmlEvent\u003e\u003e`\n- Annotates `XmlEvent` objects with their parent events that is thereafter accessible through `XmlParented.parentEvent`. Validates the nesting and throws an exception if it is invalid. \\\n  `Stream\u003cList\u003cXmlEvent\u003e\u003e withParentEvents()` on `Stream\u003cList\u003cXmlEvent\u003e\u003e`\n- From a sequence of `XmlEvent` objects filter the event sequences that form sub-trees for which a predicate returns `true`. \\\n  `Stream\u003cList\u003cXmlEvent\u003e\u003e selectSubtreeEvents(Predicate\u003cXmlStartElementEvent\u003e)` on `Stream\u003cList\u003cXmlEvent\u003e\u003e`\n- Flattens a chunked stream of objects to a stream of objects. \\\n  `Stream\u003cT\u003e flatten()` on `Stream\u003cIterable\u003cT\u003e\u003e`\n- Executes the provided callbacks on each event of this stream. \\\n  `Future forEachEvent({onText: ...})` on `Stream\u003cXmlEvent\u003e`.\n- Executes the provided callbacks on each event of this stream as a side-effect. \\\n  `Stream\u003cXmlEvent\u003e tapEachEvent({onText: ...})` on `Stream\u003cXmlEvent\u003e`.\n\nFor example, the following snippet downloads data from the Internet, converts the UTF-8 input to a Dart `String`, decodes the stream of characters to `XmlEvent`s, and finally normalizes and prints the events:\n\n```dart\nfinal url = Uri.parse('http://ip-api.com/xml/');\nfinal request = await HttpClient().getUrl(url);\nfinal response = await request.close();\nawait response\n    .transform(utf8.decoder)\n    .toXmlEvents()\n    .normalizeEvents()\n    .forEachEvent(onText: (event) =\u003e print(event.value));\n```\n\nSimilarly, the following snippet extracts sub-trees with location information from a `sitemap.xml` file, converts the XML events to XML nodes, and finally prints out the containing text:\n\n```dart\nfinal file = File('sitemap.xml');\nawait file.openRead()\n    .transform(utf8.decoder)\n    .toXmlEvents()\n    .normalizeEvents()\n    .selectSubtreeEvents((event) =\u003e event.name == 'loc')\n    .toXmlNodes()\n    .expand((nodes) =\u003e nodes)\n    .forEach((node) =\u003e print(node.innerText));\n```\n\nA common challenge when processing XML event streams is the lack of hierarchical information, thus it is very hard to figure out parent dependencies such as looking up a namespace URI. The `.withParentEvents()` transformation validates the hierarchy and annotates the events with their parent event. This enables features (such as `parentEvent` and the `namespaceUri` accessor) and makes mapping and selecting events considerably simpler. For example:\n\n```dart\nawait Stream.fromIterable([shiporderXsd])\n    .toXmlEvents()\n    .normalizeEvents()\n    .withParentEvents()\n    .selectSubtreeEvents((event) =\u003e\n        event.localName == 'element' \u0026\u0026\n        event.namespaceUri == 'http://www.w3.org/2001/XMLSchema')\n    .toXmlNodes()\n    .expand((nodes) =\u003e nodes)\n    .forEach((node) =\u003e print(node.toXmlString(pretty: true)));\n```\n\nMisc\n----\n\n### Examples\n\nThis package comes with several [examples](https://github.com/renggli/dart-xml/tree/main/example), as well as a [web demo](https://petitparser.github.io/examples/xml/xml.html).\n\nFurthermore, there are [numerous packages](https://pub.dev/packages?q=dependency%3Axml) depending on this package.\n\n### Supports\n\n- ☑ Standard well-formed XML (and HTML).\n- ☑ Reading documents using an event based API (SAX).\n- ☑ Decodes and encodes commonly used character entities.\n- ☑ Querying, traversing, and mutating API using Dart principles.\n- ☑ Querying the DOM using a subset of XPath.\n- ☑ Building XML trees using a builder API.\n\n### Limitations\n\n- ☐ Doesn't validate namespace declarations.\n- ☐ Doesn't validate schema declarations.\n- ☐ Doesn't parse, apply or enforce the DTD.\n- ☐ Doesn't support XSL or XSLT.\n\n### Standards\n\n- [Extensible Markup Language (XML) 1.0](https://www.w3.org/TR/xml/)\n- [Namespaces in XML 1.0](https://www.w3.org/TR/xml-names/)\n- [XPath 1.0](https://www.w3.org/TR/1999/REC-xpath-19991116/)\n- [W3C DOM4](https://www.w3.org/TR/domcore/)\n\n### History\n\nThis library started as an example of the [PetitParser](https://github.com/renggli/PetitParserDart) library. To my own surprise various people started to use it to read XML files. In April 2014 I was asked to replace the original [dart-xml](https://github.com/prujohn/dart-xml) library from John Evans.\n\n### License\n\nThe MIT License, see [LICENSE](https://github.com/renggli/dart-xml/raw/main/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenggli%2Fdart-xml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frenggli%2Fdart-xml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenggli%2Fdart-xml/lists"}