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

如何用JSONDecoder的unkeyedContainer解码外层为方括号的JSON数组

问题背景

给定如下JSON数组结构:

["mJv8P81",["0","4B43AC",4,1675445326,2,3,1,47.40967,8.9179,46,956,0,267,0,0,0,7,3,0,"HB-ZQI"],["0","DE007A",9,1675445313,2,5,1,50.42467,9.19857,0,518,0,180,0,0,0,5,3,0,"TEST"],["0","DD0B25",9,1675445322,2,3,1,51.9966,8.55458,39,454,-2,293,0,0,0,7,3,0,"D-HVBI"],["0","D005CE",9,1675445326,2,3,1,52.40545,9.77742,40,304,0,324,0,0,0,7,3,0,"D-HDSX"],["0","3B7B9E",4,1675445308,2,3,1,43.89118,4.92555,47,454,-2,330,0,0,0,7,3,0,"F-ZBPM"]]

需要用Foundation框架的JSONDecoder(结合unkeyedContainer)完成解码。已定义Decodable结构体,但解码后batchID和data均为nil,直接调用解码方法也报错,提示解码器期望键值对。

问题分析
  1. adslAircraft未适配数组解码:现有结构体默认按键值对解码,但JSON中每个飞机数据是无键数组,必须自定义init(from:)逻辑逐个解析数组元素。
  2. 时间戳未转换:JSON中的timestamp是秒级Unix时间戳,需处理数字到Date类型的转换,否则解码会失败。
  3. 顶层解码逻辑错误:现有udpSafeSkyPost的init(from:)尝试直接解码[adslAircraft].self,但解码器会期望嵌套数组,而实际是多个并列的数组元素,无法直接批量解码。
解决方案

1. 修正结构体实现

为adslAircraft添加自定义解码逻辑,同时完善udpSafeSkyPost的解码流程:

struct udpSafeSkyPost: Decodable {
    struct adslAircraft: Decodable {
        let version: String
        let address: String
        let address_type: UInt8
        let timestamp: Date
        let state: UInt8
        let category: UInt8
        let eStatus: UInt8
        let lat: Double
        let lon: Double
        let speed: UInt32
        let altitude: UInt32
        let vRate: Int32
        let dir: UInt32
        let sil: UInt8
        let dass: UInt8
        let navil: UInt8
        let accH: UInt8
        let accV: UInt8
        let accVeloc: UInt8
        let extID: String
        
        init(from decoder: Decoder) throws {
            var container = try decoder.unkeyedContainer()
            
            // 严格按JSON数组的顺序逐个解码元素
            version = try container.decode(String.self)
            address = try container.decode(String.self)
            address_type = try container.decode(UInt8.self)
            
            // 手动转换秒级时间戳为Date
            let timestampInt = try container.decode(Int.self)
            timestamp = Date(timeIntervalSince1970: TimeInterval(timestampInt))
            
            state = try container.decode(UInt8.self)
            category = try container.decode(UInt8.self)
            eStatus = try container.decode(UInt8.self)
            lat = try container.decode(Double.self)
            lon = try container.decode(Double.self)
            speed = try container.decode(UInt32.self)
            altitude = try container.decode(UInt32.self)
            vRate = try container.decode(Int32.self)
            dir = try container.decode(UInt32.self)
            sil = try container.decode(UInt8.self)
            dass = try container.decode(UInt8.self)
            navil = try container.decode(UInt8.self)
            accH = try container.decode(UInt8.self)
            accV = try container.decode(UInt8.self)
            accVeloc = try container.decode(UInt8.self)
            extID = try container.decode(String.self)
        }
    }
    
    let batchID: String
    let data: [adslAircraft]
    
    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        
        // 解码第一个元素作为batchID
        batchID = try container.decode(String.self)
        
        // 循环解码剩余所有飞机数组
        var aircrafts = [adslAircraft]()
        while !container.isAtEnd {
            let aircraft = try container.decode(adslAircraft.self)
            aircrafts.append(aircraft)
        }
        data = aircrafts
    }
}

2. 正确调用解码方法

设置解码器配置并执行解码(也可选择通过解码器全局配置处理时间戳,替代结构体内部的手动转换):

let jsonArrayDecoder = JSONDecoder()
// 可选:如果不在adslAircraft内部处理时间戳,可启用此全局策略
// jsonArrayDecoder.dateDecodingStrategy = .secondsSince1970

do {
    guard let data = data else {
        throw NSError(domain: "DataError", code: -1, userInfo: [NSLocalizedDescriptionKey: "数据为空"])
    }
    let safeSkyModel = try jsonArrayDecoder.decode(udpSafeSkyPost.self, from: data)
    // 解码成功后使用模型
    print("批次ID:\(safeSkyModel.batchID)")
    print("飞机数量:\(safeSkyModel.data.count)")
} catch {
    print("解码失败:\(error.localizedDescription)")
    print("详细错误信息:\(error)")
}

关键注意点

  • 无键数组解码:顶层udpSafeSkyPost和内部adslAircraft都必须通过unkeyedContainer逐个解析数组元素,不能依赖默认的键值对解码逻辑。
  • 时间戳处理:JSON中的时间戳是秒级整数,可选择在结构体内部手动转换,或设置解码器的dateDecodingStrategy为.secondsSince1970。
  • 循环解码剩余元素:顶层解码时不能直接调用decode([adslAircraft].self),因为解码器会期望嵌套数组结构,必须循环解码直到容器结束。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 22:45:45