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

Swift:如何在JSONDecoder解码Struct时预计算关联字段?

实时计算优化:解码阶段完成关联数据计算

能否在解码过程中完成计算?

完全可以。因为Structure作为顶层解码对象,在自定义init(from:)方法中,我们可以先解析出所有rooms和lists的原始数据,再基于两者的roomName关联关系完成计算,最终把结果赋值给List的calculated字段。这样就能把计算逻辑提前到数据加载阶段,避免运行时重复计算。

实现代码

我们需要调整Structure的解码逻辑,同时给List增加一个内部的原始解码结构体,用于先读取roomName,再完成计算:

struct Structure: Decodable {
    
    static func withJSON(_ fileName: String) -> Structure? {
        guard let url = Bundle.main.url(forResource: fileName, withExtension: "json", subdirectory: "Structures") else { return nil }
        guard let data = try? Data(contentsOf: url) else { return nil }
        do {
            return try JSONDecoder().decode(Structure.self, from: data)
        } catch {
            print("Unexpected error decoding Structure: \(error).")
            return nil
        }
    }
    
    private enum CodingKeys: String, CodingKey {
        case id, rooms, lists
    }
    
    let id: Int
    let rooms: [Room]
    let lists: [List]
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        rooms = try container.decode([Room].self, forKey: .rooms)
        
        // 将rooms转成字典,通过roomName快速查找对应Room
        let roomLookup = Dictionary(uniqueKeysWithValues: rooms.map { ($0.roomName, $0) })
        
        // 先解码原始list数据,再计算calculated字段
        let rawLists = try container.decode([List.RawList].self, forKey: .lists)
        lists = rawLists.map { rawList in
            guard let matchedRoom = roomLookup[rawList.roomName] else {
                // 处理找不到对应房间的情况,可根据业务调整
                return List(roomName: rawList.roomName, calculated: nil)
            }
            // 计算维度总和减去 doors*2
            let dimensionTotal = matchedRoom.dimension.reduce(0, +)
            let calculatedValue = dimensionTotal - Double(matchedRoom.doors * 2)
            return List(roomName: rawList.roomName, calculated: calculatedValue)
        }
    }
}

extension Structure {
    struct Room: Decodable {
        let roomName: String
        let dimension: [Double]
        let doors: Int
        
        // 默认Decodable即可解析,无需自定义init
        private enum CodingKeys: String, CodingKey {
            case roomName, dimension, doors
        }
    }
}

extension Structure {
    struct List {
        let roomName: String
        var calculated: Double?
        
        // 内部结构体,用于解码JSON中的原始list数据
        fileprivate struct RawList: Decodable {
            let roomName: String
            
            private enum CodingKeys: String, CodingKey {
                case roomName
            }
        }
    }
}

解码时数据暂不可用的处理方案

如果解码时出现数据不匹配(比如lists中的roomName在rooms中不存在),可以根据业务需求选择以下处理方式:

  • 默认值填充:给calculated设置默认值(如0),简化后续业务逻辑:
    return List(roomName: rawList.roomName, calculated: 0)
    
  • 抛出解码错误:确保数据完整性,直接抛出错误终止解码:
    guard let matchedRoom = roomLookup[rawList.roomName] else {
        throw DecodingError.dataCorruptedError(
            forKey: CodingKeys.lists, 
            in: container, 
            debugDescription: "Room '\(rawList.roomName)' not found in rooms array"
        )
    }
    
  • 保留可选值:让calculated保持Double?类型,后续业务中再处理nil的场景

方案优势

  • 计算逻辑提前到解码阶段,避免运行时重复计算,提升App性能
  • 数据加载完成后直接可用计算结果,无需额外处理
  • 关联逻辑集中在Structure的解码方法中,代码结构更清晰

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 09:50:25