我订阅了一个我无法控制的API,它返回一个XML结构。该结构可能非常大,如果作为一个单一的调用,需要10多秒才能返回。我有能力将其流式化(API上的一个参数),这将以块的形式返回该结构。能够在读取时处理流,将大大改善我的应用的UX。
我如何将分块的响应转化为XML块?
final url = Uri.parse(uri);
final request = await HttpClient().getUrl(url);
final response = await request.close();
final stream = response
.transform(utf8.decoder)
.transform(const xml_events.XmlEventDecoder())
.transform(const xml_events.XmlNormalizer())
.expand((events) => events)
.forEach((event) => log.info('Stream Receipt: $event'));
}
这将为我返回各个形式良好的XML块,如
<c>
36
</c>
但我真正需要的是构成一个对象的块,例如
<a id="1">
<b name="Joe">
<c>36</c>
</b>
</a>
看来我需要一个 transform
方法,并能够指定 <a>
作为我的划分标签,这样子元素就会被分组,并在完成后流转。
下面的代码有点笨拙,但只用简单的操作符就能在单个流中实现你所需要的功能。
input
.transform(const XmlEventDecoder())
.transform(const XmlNormalizer())
.expand((events) => events)
// skip to the start of the container
.skipWhile((event) => !(event is XmlStartElementEvent && event.name == 'root'))
// skip over the container element
.skip(1)
// consume until the end of the container
.takeWhile((event) => !(event is XmlEndElementEvent && event.name == 'root'))
// create a forest of sub-trees
.map((event) => [event])
.transform(const XmlNodeDecoder())
.expand((elements) => elements)
// filter out the text elements between the sub-trees
.where((element) => element is XmlElement)
// do something with the sub-trees
.listen((element) => ...);