Skip to content

Commit 7205d2b

Browse files
committed
[XPath] Implement fn:serialize, fn:parse-xml, and fn:parse-xml-fragment
16211 72.0% successes 5 0.0% skipped 4011 17.8% failures 2287 10.2% errors
1 parent 59e8603 commit 7205d2b

File tree

2 files changed

+63
-6
lines changed

2 files changed

+63
-6
lines changed

lib/src/xpath/functions/accessor.dart

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import '../../xml/nodes/attribute.dart';
2+
import '../../xml/nodes/document.dart';
3+
import '../../xml/nodes/document_fragment.dart';
24
import '../../xml/nodes/element.dart';
35
import '../../xml/nodes/node.dart';
46
import '../../xml/nodes/processing.dart';
@@ -169,7 +171,13 @@ XPathSequence _fnSerialize(
169171
XPathContext context,
170172
XPathSequence arg, [
171173
Object? params,
172-
]) => throw UnimplementedError('fn:serialize');
174+
]) {
175+
// TODO: Add support for serialization parameters
176+
final result = arg
177+
.map((item) => item is XmlNode ? item.toXmlString() : xsString.cast(item))
178+
.join();
179+
return XPathSequence.single(result);
180+
}
173181

174182
/// https://www.w3.org/TR/xpath-functions-31/#func-parse-xml
175183
const fnParseXml = XPathFunctionDefinition(
@@ -185,8 +193,10 @@ const fnParseXml = XPathFunctionDefinition(
185193
function: _fnParseXml,
186194
);
187195

188-
XPathSequence _fnParseXml(XPathContext context, String? arg) =>
189-
throw UnimplementedError('fn:parse-xml');
196+
XPathSequence _fnParseXml(XPathContext context, String? arg) {
197+
if (arg == null) return XPathSequence.empty;
198+
return XPathSequence.single(XmlDocument.parse(arg));
199+
}
190200

191201
/// https://www.w3.org/TR/xpath-functions-31/#func-parse-xml-fragment
192202
const fnParseXmlFragment = XPathFunctionDefinition(
@@ -202,8 +212,10 @@ const fnParseXmlFragment = XPathFunctionDefinition(
202212
function: _fnParseXmlFragment,
203213
);
204214

205-
XPathSequence _fnParseXmlFragment(XPathContext context, String? arg) =>
206-
throw UnimplementedError('fn:parse-xml-fragment');
207-
208215
XmlNode _defaultToContextItem(XPathContext context) =>
209216
xsNode.cast(context.item);
217+
218+
XPathSequence _fnParseXmlFragment(XPathContext context, String? arg) {
219+
if (arg == null) return XPathSequence.empty;
220+
return XPathSequence.single(XmlDocumentFragment.parse(arg));
221+
}

test/xpath/functions/accessor_test.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,49 @@ void main() {
8686
isXPathSequence(isEmpty),
8787
);
8888
});
89+
test('fn:serialize', () {
90+
expect(
91+
fnSerialize(context, [
92+
XPathSequence([
93+
document.findAllElements('a').first,
94+
'text',
95+
document.findAllElements('b').first,
96+
]),
97+
]),
98+
isXPathSequence(['<a>1</a>text<b>2</b>']),
99+
);
100+
expect(fnSerialize(context, [XPathSequence.empty]), isXPathSequence(['']));
101+
});
102+
test('fn:parse-xml', () {
103+
expect(
104+
fnParseXml(context, [const XPathSequence.single('<r><a>1</a></r>')]),
105+
isXPathSequence([isA<XmlDocument>()]),
106+
);
107+
expect(
108+
fnParseXml(context, [XPathSequence.empty]),
109+
isXPathSequence(isEmpty),
110+
);
111+
expect(
112+
() => fnParseXml(context, [const XPathSequence.single('<r>unclosed')]),
113+
throwsA(isA<XmlException>()),
114+
);
115+
});
116+
test('fn:parse-xml-fragment', () {
117+
expect(
118+
fnParseXmlFragment(context, [
119+
const XPathSequence.single('<a>1</a><b>2</b>'),
120+
]),
121+
isXPathSequence([isA<XmlDocumentFragment>()]),
122+
);
123+
expect(
124+
fnParseXmlFragment(context, [XPathSequence.empty]),
125+
isXPathSequence(isEmpty),
126+
);
127+
expect(
128+
() => fnParseXmlFragment(context, [
129+
const XPathSequence.single('<r>unclosed'),
130+
]),
131+
throwsA(isA<XmlException>()),
132+
);
133+
});
89134
}

0 commit comments

Comments
 (0)