【问题标题】:How to group XML elements from a HttpClient stream in Flutter如何在 Flutter 中对 HttpClient 流中的 XML 元素进行分组
【发布时间】:2020-05-12 00:45:03
【问题描述】:

我订阅了一个我无法控制的 API,它返回一个 XML 结构。该结构可能非常大,如果作为单个调用拉出,则需要 10 多秒才能返回。我有能力流式传输它(API 上的一个参数),它将以块的形式返回结构。能够在读取流时对其进行处理将大大改善我的应用的用户体验。

如何将块的响应转换为 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 方法,并且能够指定&lt;a&gt; 作为我的描述标签,以便子元素在完成时进行分组和流式传输?

【问题讨论】:

  • 你需要XmlNodeCodec,像这样:Stream.value('&lt;root&gt;&lt;c&gt;c1&lt;/c&gt;&lt;c&gt;c2&lt;/c&gt;&lt;c&gt;c3&lt;/c&gt;&lt;/root&gt;') .transform(XmlEventCodec().decoder.fuse(XmlNodeCodec().decoder)) .listen((nodes) { print(nodes); nodes.forEach((node) { node.children.forEach(print); }); });
  • 我看到这创建了一个新的流 1 子级深度以从 中拉出 节点。我将如何深入 2 个子级别,以便从 &lt;root&gt;&lt;a&gt;&lt;b&gt;&lt;/b&gt;&lt;c&gt;c1&lt;/c&gt;&lt;c&gt;c2&lt;/c&gt;&lt;c&gt;c3&lt;/c&gt;&lt;/a&gt;&lt;/root&gt; 拉出 &lt;c&gt; 元素?我是否只需要知道它是第 n 个孩子,还是我可以指定某种过滤器仅将 &lt;c&gt; 元素拉入流中以收听?有没有关于转换、解码器、保险丝等的体面文档?我找到了 Flutter/Dart 文档,但它们有点简洁,没有真正的例子可言。

标签: xml flutter dart stream


【解决方案1】:

以下代码有点笨拙,但仅使用简单的运算符即可在单个流中完成您需要的操作:

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) => ...);

【讨论】:

  • 感谢 cmets - 这很有帮助。有没有办法一次“取 n 个元素”来触发 .listen 事件?如果调用返回 1,000 个条目,我想一次处理 100 个超过 10 个批处理。如果返回 750,我会做 7 批 100,然后排空最后 50 个。我正在向谷歌地图添加标记,并且每次监听事件都重绘太多了。
  • 通过将结果放入 List 来实现上述目标。当 List.length > 100 时,我将该列表复制到另一个列表中,并将其传递给我的“外部侦听器”以进行流式传输。清除当前列表并继续。笨重,但有效。
猜你喜欢
  • 1970-01-01
  • 2019-05-30
  • 2016-02-21
  • 2015-03-12
  • 2012-11-24
  • 2012-03-12
  • 2019-10-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多