Flutter XML解析插件xml2的使用
Flutter XML解析插件xml2的使用
Dart XML
Dart XML 是一个轻量级库,用于解析、遍历、查询、转换和构建XML文档。
这个库是开源的、稳定的并且经过了良好的测试。开发在 GitHub 上进行。你可以在此报告问题或创建拉取请求。通用问题最好在 StackOverflow 上提问。
该包托管在 dart packages 上。每次发布都会生成最新的类文档。
安装
遵循 dart packages 上的安装说明。
在你的 Dart 代码中导入库:
import 'package:xml/xml.dart';
⚠️ 这个库广泛使用了静态扩展方法。如果你使用库前缀或者仅选择性地显示类,可能会错过一些功能。由于历史原因,公共类具有 Xml
前缀,因此与其他代码的冲突应该很少。
读取和写入
要读取 XML 输入,可以使用工厂方法 XmlDocument.parse(String input)
:
final bookshelfXml = '''<?xml version="1.0"?>
<bookshelf>
<book>
<title lang="en">Growing a Language</title>
<price>29.99</price>
</book>
<book>
<title lang="en">Learning XML</title>
<price>39.95</price>
</book>
<price>132.00</price>
</bookshelf>''';
final document = XmlDocument.parse(bookshelfXml);
结果对象是一个 XmlDocument
的实例。如果文档无法解析,则会抛出 XmlParserException
异常。
要将解析后的 XML 文档写回,只需调用 toString()
或者如果需要更多控制则调用 toXmlString(...)
:
print(document.toString());
print(document.toXmlString(pretty: true, indent: '\t'));
要从文件中读取 XML,请使用 dart:io
库:
final file = new File('bookshelf.xml');
final document = XmlDocument.parse(file.readAsStringSync());
如果文件不是 UTF-8 编码,请在 readAsStringSync
中传递正确的编码。对于读取和写入大型文件,你可能需要使用事件驱动的 API。
遍历和查询
访问器允许访问 XML 树中的节点:
attributes
返回节点的属性。children
返回节点的直接子节点。
这两个列表都是可变的,并支持所有常见的 List
方法,例如 add(XmlNode)
、addAll(Iterable<XmlNode>)
、insert(int, XmlNode)
和 insertAll(int, Iterable<XmlNode>)
。尝试添加 null
值或不支持的节点类型会抛出 XmlNodeTypeError
错误。已经作为树一部分的节点不会自动移动,你需要先创建一个副本,否则会抛出 XmlParentError
异常。XmlDocumentFragment
节点会自动展开并添加其子节点的副本。
有方法沿着不同的轴遍历 XML 树:
siblings
返回在同一级别上位于当前节点之前和之后的节点的可迭代对象。preceding
返回位于当前节点开头标签之前的节点的可迭代对象。descendants
返回位于当前节点之后的节点的可迭代对象。following
返回位于当前节点结尾标签之后的节点的可迭代对象。ancestors
返回当前节点的祖先节点的可迭代对象,即父节点、祖父节点等。注意,这是唯一一个以逆文档顺序遍历节点的可迭代对象。
例如,可以使用 descendants
迭代器来提取 XML 树中的所有文本内容:
final textual = document.descendants
.where((node) => node is XmlText && node.text.trim().isNotEmpty)
.join('\n');
print(textual);
还有一些方便的助手只过滤元素节点:childElements
、siblingElements
、precedingElements
、descendantElements
、followingElements
和 ancestorElements
。
此外,还有一些帮助程序来查找具有特定标签的元素:
getElement(String name)
查找具有提供的name
标签的第一个直接子节点,或者返回null
。findElements(String name)
查找具有提供的name
标签的当前节点的直接子节点。findAllElements(String name)
查找具有提供的name
标签的当前节点的直接和间接子节点。
例如,要查找所有 <title>
标签的节点,可以这样写:
final titles = document.findAllElements('title');
上述代码返回一个惰性迭代器,递归遍历 XML 文档并返回具有请求标签名的所有元素节点。要提取文本内容,可以调用 text
:
titles
.map((node) => node.text)
.forEach(print);
这将打印 Growing a Language
和 Learning XML
。
同样,为了计算所有书籍的总价,可以编写以下表达式:
final total = document.findAllElements('book')
.map((node) => double.parse(node.findElements('price').single.text))
.reduce((a, b) => a + b);
print(total);
注意,这首先找到所有的书籍,然后提取价格,以避免计算包含在书架中的价格标签。
构建
虽然可以手动实例化和组合 XmlDocument
、XmlElement
和 XmlText
节点,但 XmlBuilder
提供了一个简单的流畅 API 来构建完整的 XML 树。为了创建上面的书架示例,可以这样写:
final builder = XmlBuilder();
builder.processing('xml', 'version="1.0"');
builder.element('bookshelf', nest: () {
builder.element('book', nest: () {
builder.element('title', nest: () {
builder.attribute('lang', 'en');
builder.text('Growing a Language');
});
builder.element('price', nest: 29.99);
});
builder.element('book', nest: () {
builder.element('title', nest: () {
builder.attribute('lang', 'en');
builder.text('Learning XML');
});
builder.element('price', nest: 39.95);
});
builder.element('price', nest: '132.00');
});
final document = builder.buildDocument();
element
方法支持可选命名参数:
- 最常见的是
nest:
参数,用于将内容插入元素中。通常,这将是一个调用builder
方法来定义属性、声明命名空间和添加子元素的函数。但是,该参数也可以是一个字符串或任意 Dart 对象,将其转换为字符串并作为文本节点添加。 - 为了简化,还有
attributes:
参数,它接受一个映射来定义简单的键值对。 - 此外,我们可以使用
namespace:
提供元素的命名空间 URI 并使用namespaces:
声明新的命名空间前缀。有关详细信息,请参阅该方法的文档。
构建器模式允许你轻松地将重复的部分提取到特定的方法中。在上面的例子中,可以将写一本书的部分放到一个单独的方法中,如下所示:
void buildBook(XmlBuilder builder, String title, String language, num price) {
builder.element('book', nest: () {
builder.element('title', nest: () {
builder.attribute('lang', language);
builder.text(title);
});
builder.element('price', nest: price);
});
}
上述 buildDocument()
方法返回构建的文档。要将构建的节点附加到现有的 XML 文档中,可以使用 buildFragment()
。一旦构建器返回构建的节点,其内部状态将被重置。
final builder = XmlBuilder();
buildBook(builder, 'The War of the Worlds', 'en', 12.50);
buildBook(builder, 'Voyages extraordinaries', 'fr', 18.20);
document.rootElement.children.add(builder.buildFragment());
事件驱动
读取大型 XML 文件并将它们的 DOM 实例化到内存中可能是昂贵的。作为替代方案,此库提供了使用 Dart 迭代器或流将 XML 文档读取和转换为事件序列的可能性。这些方法类似于其他库中已知的事件驱动 SAX 解析。
import 'package:xml/xml_events.dart';
迭代器
在最简单的情况下,你可以通过以下代码获得一个 Iterable<XmlEvent>
过滤输入字符串中的事件。这会懒加载解析输入,只有在请求时才会解析输入:
parseEvents(bookshelfXml)
.whereType<XmlTextEvent>()
.map((event) => event.text.trim())
.where((text) => text.isNotEmpty)
.forEach(print);
这种方法需要整个输入在开始时可用,并且如果数据本身是异步可用的(例如来自慢速网络连接),则不起作用。更复杂的方法是使用 Dart 流。
流
要从文件或 HTTP 流异步解析和处理事件,可以使用提供的编解码器在字符串、事件和 DOM 树节点之间转换:
-
编解码器:
XmlEventCodec
- 将字符串解码为一系列
XmlEvent
对象。Stream<List<XmlEvent>> toXmlEvents()
在Stream<String>
- 将一系列
XmlEvent
对象编码为字符串。Stream<String> toXmlString()
在Stream<List<XmlEvent>>
- 将字符串解码为一系列
-
编解码器:
XmlNodeCodec
- 将一系列
XmlEvent
对象解码为XmlNode
对象。Stream<List<XmlNode>> toXmlNodes()
在Stream<List<XmlEvent>>
- 将一系列
XmlNode
对象编码为XmlEvent
对象。Stream<List<XmlEvent>> toXmlEvents()
在Stream<List<XmlNode>>
- 将一系列
提供了一些转换来简化处理复杂的流:
- 通过删除空事件和合并相邻的文本事件来规范化一系列
XmlEvent
对象。Stream<List<XmlEvent>> normalizeEvents()
在Stream<List<XmlEvent>>
- 使用其父事件注释
XmlEvent
对象,使其可通过XmlParented.parentEvent
访问。验证嵌套并抛出异常如果无效。Stream<List<XmlEvent>> withParentEvents()
在Stream<List<XmlEvent>>
- 从一系列
XmlEvent
对象中过滤形成子树的事件序列,其中谓词返回true
。Stream<List<XmlEvent>> selectSubtreeEvents(Predicate<XmlStartElementEvent>)
在Stream<List<XmlEvent>>
- 将分块流的对象展平为对象流。
Stream<T> flatten()
在Stream<Iterable<T>>
- 在此流上的每个事件上执行提供的回调。
Future forEachEvent({onText: ...})
在Stream<XmlEvent>
例如,以下代码段从互联网下载数据,将 UTF-8 输入转换为 Dart 字符串,将字符流解码为 XmlEvent
,最后规范化并打印事件:
final url = Uri.parse('http://ip-api.com/xml/');
final request = await HttpClient().getUrl(url);
final response = await request.close();
await response
.transform(utf8.decoder)
.toXmlEvents()
.normalizeEvents()
.forEachEvent(onText: (event) => print(event.text));
同样,以下代码片段从 sitemap.xml
文件中提取位置信息,将 XML 事件转换为 XML 节点,最后打印包含的文本:
final file = File('sitemap.xml');
await file.openRead()
.transform(utf8.decoder)
.toXmlEvents()
.selectSubtreeEvents((event) => event.name == 'loc')
.toXmlNodes()
.expand((nodes) => nodes)
.forEach((node) => print(node.innerText));
处理 XML 事件流的一个常见挑战是缺乏层次结构信息,因此很难找出诸如查找命名空间 URI 的父依赖关系。.withParentEvents()
转换验证层次结构并注释事件及其父事件。这使功能(如 parentEvent
和 namespaceUri
访问器)变得简单得多,并使映射和选择事件更加简单。例如:
await Stream.fromIterable([shiporderXsd])
.toXmlEvents()
.withParentEvents()
.selectSubtreeEvents((event) =>
event.localName == 'element' &&
event.namespaceUri == 'http://www.w3.org/2001/XMLSchema')
.toXmlNodes()
.expand((nodes) => nodes)
.forEach((node) => print(node.toXmlString(pretty: true)));
更多关于Flutter XML解析插件xml2的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter XML解析插件xml2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在 Flutter 中,xml
是一个常用的 XML 解析库,它提供了简单易用的 API 来解析和生成 XML 文档。xml
库的完整名称是 xml
, 你可以在 pubspec.yaml
文件中添加依赖来使用它。
1. 添加依赖
首先,在你的 pubspec.yaml
文件中添加 xml
依赖:
dependencies:
flutter:
sdk: flutter
xml: ^6.1.0 # 请使用最新版本
然后运行 flutter pub get
来获取依赖。
2. 解析 XML 文档
假设你有一个 XML 字符串如下:
<bookstore>
<book>
<title lang="en">Learning Flutter</title>
<author>John Doe</author>
<year>2023</year>
<price>29.99</price>
</book>
<book>
<title lang="es">Aprendiendo Flutter</title>
<author>Jane Smith</author>
<year>2022</year>
<price>24.99</price>
</book>
</bookstore>
你可以使用 xml
库来解析这个 XML 字符串:
import 'package:xml/xml.dart' as xml;
void main() {
String xmlString = '''
<bookstore>
<book>
<title lang="en">Learning Flutter</title>
<author>John Doe</author>
<year>2023</year>
<price>29.99</price>
</book>
<book>
<title lang="es">Aprendiendo Flutter</title>
<author>Jane Smith</author>
<year>2022</year>
<price>24.99</price>
</book>
</bookstore>
''';
final document = xml.XmlDocument.parse(xmlString);
// 获取所有 <book> 元素
final books = document.findAllElements('book');
for (var book in books) {
final title = book.findElements('title').single.text;
final lang = book.findElements('title').single.getAttribute('lang');
final author = book.findElements('author').single.text;
final year = book.findElements('year').single.text;
final price = book.findElements('price').single.text;
print('Title: $title, Lang: $lang');
print('Author: $author');
print('Year: $year');
print('Price: $price');
print('---');
}
}
3. 生成 XML 文档
你也可以使用 xml
库来生成 XML 文档:
import 'package:xml/xml.dart' as xml;
void main() {
final builder = xml.XmlBuilder();
builder.processing('xml', 'version="1.0"');
builder.element('bookstore', nest: () {
builder.element('book', nest: () {
builder.element('title', attributes: {'lang': 'en'}, nest: 'Learning Flutter');
builder.element('author', nest: 'John Doe');
builder.element('year', nest: '2023');
builder.element('price', nest: '29.99');
});
builder.element('book', nest: () {
builder.element('title', attributes: {'lang': 'es'}, nest: 'Aprendiendo Flutter');
builder.element('author', nest: 'Jane Smith');
builder.element('year', nest: '2022');
builder.element('price', nest: '24.99');
});
});
final xmlDocument = builder.buildDocument();
print(xmlDocument.toXmlString(pretty: true));
}