You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dart解析JSON报FormatException: Unexpected character错误

问题根因

报错和Dart的JSON格式化能力无关,核心原因是本地存储的JSON文件格式非法,同时现有代码存在逻辑缺陷会持续触发这类问题:

  • 从贴出的文件内容可以看到,在第722个字符位置出现了}]{结构:前半段是包含2个商品的JSON数组[{...},{...}],数组闭合符]后直接拼接了第3个商品的JSON对象开头{,最后又多了一个数组闭合符]。标准JSON仅允许单个根节点,这种多段JSON拼接、括号不匹配的内容无法被jsonDecode解析,直接抛出格式异常。
  • 这类损坏数据一般是历史写入逻辑误用了FileMode.append追加模式导致:每次写入新商品时没有覆盖旧的完整JSON,而是把新的JSON字符串直接拼接到旧文件末尾,最终形成非法格式。
  • 现有代码存在逻辑漏洞:内存中维护的静态集合_shoppingListSet在APP冷启动后为空,读取文件时没有把解析出的商品数据同步回这个集合,后续写入、删除操作都是基于空集合执行,要么覆盖丢失旧数据,要么配合错误的追加模式持续生成损坏的JSON。
修复方案
  • 第一步:清理存量损坏数据
    直接删除应用本地的旧存储文件,或者手动修正文件内容为合法JSON格式:把所有商品对象放到同一个根数组[]内,对象之间用逗号分隔,删除多余的][拼接字符,示例正确格式:
    [
      {"ID":0, "product":{...}, "ticked":false},
      {"ID":1, "product":{...}, "ticked":false},
      {"ID":2, "product":{...}, "ticked":false}
    ]
    
  • 第二步:修复代码逻辑漏洞
    修改readShoppingListProducts方法,读取文件后把解析出的商品列表同步到内存集合,保证内存数据和文件数据一致,修正后的代码如下:
    Future<List<ShoppingListProduct>> readShoppingListProducts() async {
      final File fl = await file;
      final _content = await fl.readAsString();
    
      List<dynamic> _jsonData = [];
      if(_content.isNotEmpty){
        _jsonData = jsonDecode(_content);
      }
    
      final List<ShoppingListProduct> _shoppingListProducts = _jsonData
          .map(
            (e) => ShoppingListProduct.fromJson(e as Map<String, dynamic>),
          )
          .toList();
      // 关键修复:同步数据到内存集合
      _shoppingListSet = _shoppingListProducts.toSet();
      return _shoppingListProducts;
    }
    
    注意:APP启动后、第一次执行写入/删除/更新操作前,必须先调用一次readShoppingListProducts完成内存数据初始化,避免内存集合为空导致的数据覆盖问题。也可以在文件初始化完成后自动触发读取逻辑,减少手动调用遗漏。
    写入操作保持默认的覆盖模式即可(writeAsString默认使用FileMode.write,会全量覆盖旧文件内容),不要使用追加模式写JSON数组,避免再次出现拼接问题。
  • 第三步:(可选)增加异常容错
    读取文件时增加格式异常捕获,遇到损坏文件时自动备份并重置为空文件,避免APP直接崩溃:
    Future<List<ShoppingListProduct>> readShoppingListProducts() async {
      final File fl = await file;
      final _content = await fl.readAsString();
    
      List<dynamic> _jsonData = [];
      if(_content.isNotEmpty){
        try {
          _jsonData = jsonDecode(_content);
        } on FormatException {
          // 备份损坏的旧文件,方便后续排查
          final backupFile = File('${fl.path}.bak.${DateTime.now().millisecondsSinceEpoch}');
          await fl.copy(backupFile.path);
          // 重置为空的合法JSON数组
          await fl.writeAsString(jsonEncode([]));
        }
      }
    
      final List<ShoppingListProduct> _shoppingListProducts = _jsonData
          .map(
            (e) => ShoppingListProduct.fromJson(e as Map<String, dynamic>),
          )
          .toList();
      _shoppingListSet = _shoppingListProducts.toSet();
      return _shoppingListProducts;
    }
    
验证方式
  • 写入多个商品后打开本地存储文件,确认整个文件是单个完整JSON数组,所有商品对象都在同一个[]内,没有多余的拼接字符
  • 冷重启APP后读取商品列表,确认无格式异常,之前写入的商品全部存在
  • 多次执行新增、删除、更新操作后重启APP,确认数据不丢失、文件格式始终合法

内容的提问来源于stack exchange,提问作者Jack .T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 13:39:18