【问题标题】:How to decode large json array in flutter如何在颤动中解码大型json数组
【发布时间】:2020-09-12 14:55:37
【问题描述】:

我有一个相当大的 json 文件,我使用我的 Flutter 应用程序从一个大约 200MB 的 web api 下载了该文件

final response = Dio().get(uri, options: Options(responseType: ResponseType.plain));

(使用默认的 Dio 选项也使用 json 解码,导致内存不足异常。 这不是我的问题的直接部分,但也许有帮助)

现在问题在获取json字符串后开始。 当我在响应正文上使用 jsonDecode 时,我的内存不足。 (也许 Dio 使用的功能相同?)

final data = jsonDecode(response.body); 内存不足

json 对象本身是一个数组,里面有很多项,格式如下:

[
  {"data": {independent data1}},
  {"data": {independent data2}},
  ...
]

我会很好地一次解码一项而不是一次解码所有内容以减少内存使用。有没有另一种方法来处理这个数组中的所有项目?类似的东西

jsonArrayDecode(response.body, onItemDecode: (item) { /*do stuff with item */ });

还是我必须自己写一个可以顺序解码的json阅读器?

【问题讨论】:

  • 尝试将JsonDecoder 用作StreamTransformer
  • 我不确定您将 JsonDecoder 用作 StreamTransformer 到底是什么意思,但经过一些试验后,我将 Dio Transformer 替换为自定义的,并将 transformResponse 函数覆盖为 Future transformResponse(RequestOptions options, ResponseBody response) { return JsonDecoder().bind(utf8.decoder.bind(response.stream)).first; },它的工作原理不够坠毁。在 GC 中断的情况下处理 json 的速度仍然很慢,但也许这已经很好了。感谢您的意见!
  • 我的意思不是Dio Transformer - 我的意思是Stream.transform()StreamTransformer 为参数的方法,更多信息在这里:dart.dev/articles/archive/converters-and-codecs
  • 类似:Stream.value('[{"header": {"header1": -1}}, ' + '{"k1": 111, "k2": 222}, {"k3": 333}, ' * 2 + ' {"footer": 999}]') .transform(JsonDecoder(reviver)) .listen((data) { print('=' * 64); print(data); }); - 这里 reviver 用于在解析单个项目时选择它们 - 这意味着理论上您可以拥有无​​限的输入数据流并在项目到来时对其进行处理
  • 嗯,好吧,但是如果使用它,那么来自listenonData 函数只会在整个列表已经转换时调用一次,或者我可以使用恢复器以产生更多输出?或者我可以忽略监听并只处理 reviver 中的数据吗?类似response.data.stream.cast<List<int>>().transform(utf8.decoder).transform(JsonDecoder((key, value) { if (/*object is top level item*/) processItem(); return null; else return value }))?

标签: json flutter


【解决方案1】:

感谢@pskink,我设法解决了我的问题。我使用来自 Dio 的ResponseType.stream 来获取可以使用来自JsonDecoder 的 reviver 函数参数进行处理的流。我处理reviver中的所有数据,忽略listener的onData函数中的数据。

...
final response = await dio.get(uri,
        options: Options(responseType: ResponseType.stream);
Function reviver = (key, value) {
  if (/*value is element from top level list*/) {
    processItem(value);
    return null;
  }
  return value;
}
final completer = Completer();
response.data.stream.cast<List<int>>()
        .transform(utf8.decoder)
        .transform(JsonDecoder(reviver))
        .listen(null,  onDone: () { completer.complete(); });
await completer.future;
...

【讨论】:

  • 与返回 value 的级别 >0 相比,您实际上想如何检查您是否处于返回 null 的级别 0?
  • 不幸的是,我没有针对一般情况的解决方案,但在我的情况下,根对象和只有根对象具有键值对 {"object": "card"},所以我只需检查它是否存在。
  • 好吧,我明白了,顺便说一句,使用JsonCodec(reviver: reviver).fuse(utf8),因为它在引擎盖下产生组合_JsonUtf8Decoder,这是二合一解决方案
  • 如果我们在这里没有问题,我仍然不明白。即使我们确定了我们的“根”级项目,并在 reviver 中返回 null,最后一次调用 reviver 也会得到值,它是初始巨大列表大小的 null 数组。我相信空值数组也需要大量内存。有什么想法吗?
  • 我会说这取决于这个初始巨大列表的数量,但假设列表有 1000000 个元素并且 null 值需要 8 个字节,但仍然只需要 8MB
猜你喜欢
  • 1970-01-01
  • 2020-07-23
  • 2021-11-13
  • 1970-01-01
  • 1970-01-01
  • 2020-08-27
  • 2021-06-26
  • 2020-10-28
  • 1970-01-01
相关资源
最近更新 更多