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

iOS应用JSON读写报错:JSONSerialization Invalid value at character 0

解决iOS JSON写入后读取崩溃的问题

嘿,我一眼就瞅出问题所在了——你试图往App的Bundle目录里写文件,但这个目录是只读的!

问题根源

App安装后,Bundle里的所有文件都是只读的,你调用dict.write(to: url, atomically: true)其实根本没成功写入(或者写了个无效的内容),导致再次读取时拿到的是损坏的数据,JSON解析直接崩溃,就出现了你看到的Invalid value around character 0错误。而初始读取正常是因为Bundle里的原始JSON文件是完好的。

解决方案:改用Documents目录读写

App的沙盒里只有Documents目录是可读写的,我们需要把JSON文件放到这里来操作。下面是修正后的完整代码:

1. 先写个工具方法获取Documents里的文件URL

private func getSaveDataURL() -> URL {
    // 获取Documents目录路径
    let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    // 返回SaveData.json的完整URL
    return documentsDir.appendingPathComponent("SaveData.json")
}

2. 修改读取按钮的代码(添加错误处理,避免崩溃)

@IBAction func UpdateLabelTapped(_ sender: UIButton) {
    let url = getSaveDataURL()
    do {
        let data = try Data(contentsOf: url)
        // 用Swift原生字典替代NSDictionary,类型更安全
        if let jsonDict = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any],
           let message = jsonDict["message"] as? String {
            self.DataLabel.text = message
        } else {
            self.DataLabel.text = "无有效数据"
        }
    } catch {
        // 处理文件不存在或解析错误的情况(比如第一次运行)
        self.DataLabel.text = "初始默认内容"
        // 可选:第一次运行时写入默认数据
        let defaultData = try! JSONSerialization.data(withJSONObject: ["message": "初始默认内容"], options: .prettyPrinted)
        try! defaultData.write(to: url)
    }
}

3. 修改写入按钮的代码(用标准JSON序列化)

@IBAction func WriteToJSONTapped(_ sender: UIButton) {
    let url = getSaveDataURL()
    // 用Swift原生字典,避免OC桥接的潜在问题
    let contentDict: [String: Any] = ["message": "Writing JSON!!"]
    
    do {
        // 手动生成标准JSON数据,比NSDictionary的write方法更可靠
        let jsonData = try JSONSerialization.data(withJSONObject: contentDict, options: .prettyPrinted)
        // 原子写入,保证文件完整性
        try jsonData.write(to: url, options: .atomic)
        print("JSON写入成功!")
    } catch {
        print("写入失败:\(error.localizedDescription)")
    }
}

可选:复制Bundle里的初始JSON到Documents

如果你希望第一次运行时读取Bundle里的初始JSON内容,可以在App启动时添加复制逻辑:

private func copyInitialSaveDataIfNeeded() {
    let targetURL = getSaveDataURL()
    // 如果Documents里已经有文件,就不用复制了
    guard !FileManager.default.fileExists(atPath: targetURL.path) else { return }
    
    // 从Bundle获取初始文件
    if let sourceURL = Bundle.main.url(forResource: "SaveData", withExtension: "json") {
        do {
            try FileManager.default.copyItem(at: sourceURL, to: targetURL)
            print("初始JSON文件复制成功")
        } catch {
            print("复制初始文件失败:\(error)")
        }
    }
}

然后在AppDelegate的didFinishLaunchingWithOptions或者SceneDelegate的sceneDidBecomeActive里调用这个方法即可。

额外提示

  • 尽量避免使用try!,改用do-catch处理错误,这样App不会因为小问题直接崩溃。
  • 优先使用Swift原生的[String: Any]字典,而不是NSMutableDictionary,类型更安全,也符合Swift的开发习惯。

内容的提问来源于stack exchange,提问作者Mrosky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:23:31