-
Notifications
You must be signed in to change notification settings - Fork 0
/
xml.dart
83 lines (66 loc) · 2.61 KB
/
xml.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Copyright (c) 2021 Mantano. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:xml/xml.dart' as xml;
/// Wrapper around the XML package that adds a better API, basic XPath queries
/// handling namespace prefixes.
abstract class XmlNode<Node extends xml.XmlNode> {
XmlNode(this._node, {Map<String, String>? prefixes})
: prefixes = prefixes ?? {};
final Node _node;
/// Maps a namespace prefix to its URI.
Map<String, String> prefixes;
List<XmlElement> xpath(String xpath) => _xpathString(xpath);
XmlElement? firstXPath(String xpath) {
List<XmlElement> elements = _xpathString(xpath, onlyFirst: true);
return (elements.isEmpty) ? null : elements.first;
}
List<XmlElement> _xpathString(String xpath, {bool onlyFirst = false}) => xpath
.split('|')
.expand((path) => _xpath(path.split('/'), onlyFirst: onlyFirst))
.toList();
List<XmlElement> _xpath(List<String> paths,
{bool relative = true, bool onlyFirst = false}) {
if (paths.isEmpty) {
return [if (this is XmlElement) this as XmlElement];
}
String path = paths.removeAt(0);
if (path.isEmpty) {
return _xpath(paths, relative: false, onlyFirst: onlyFirst);
}
RegExpMatch? match = RegExp(r'(?:([-\w]+)\:)?([-\w\*]+)').firstMatch(path);
if (match == null) {
return [];
}
String prefix = match.group(1)!;
String tag = match.group(2)!;
String? namespace = prefixes[prefix];
Iterable<xml.XmlElement> elements = relative
? _node.findElements(tag, namespace: namespace)
: _node.findAllElements(tag, namespace: namespace);
if (onlyFirst && elements.isNotEmpty) {
elements = [elements.first];
}
return elements
.expand((e) => XmlElement(e, prefixes: prefixes)
._xpath(paths, onlyFirst: onlyFirst))
.toList();
}
}
class XmlDocument extends XmlNode<xml.XmlDocument> {
XmlDocument(xml.XmlDocument _node, {Map<String, String>? prefixes})
: super(_node, prefixes: prefixes);
factory XmlDocument.parse(String input) =>
XmlDocument(xml.XmlDocument.parse(input));
}
class XmlElement extends XmlNode<xml.XmlElement> {
XmlElement(xml.XmlElement _node, {Map<String, String>? prefixes})
: super(_node, prefixes: prefixes);
String get name => _node.name.local;
String get text => _node.text;
String? operator [](String name) => getAttribute(name);
String? getAttribute(String name, {String? namespace}) {
namespace = prefixes[namespace] ?? namespace;
return _node.getAttribute(name, namespace: namespace);
}
}