解码 JSON 时 Swift 结构解码器初始化程序错误

问题描述 投票:0回答:1

我试图更好地理解解码器初始化器和 JSON 解码。此代码运行并产生预期结果:

let json = """
[
    {
        "firstName": "Jon",
        "lastName": "Dough"
   },
    {
        "firstName": "Jane",
        "lastName": "Dough"
    }
]
""".data(using: .utf8)!

struct Character: Codable {
    var firstName: String
    var lastName: String
    
     enum CodingKeys: String, CodingKey {
        case
            firstName,
            lastName
    }
 
    init(from decoder: Decoder) throws {
        
        let data = try decoder.container(keyedBy: CodingKeys.self)

        firstName = try data.decode(String.self, forKey: .firstName)
        lastName = try data.decode(String.self, forKey: .lastName)
    }
}

let decoder = JSONDecoder()
func doDecode() -> [Character] {
    do {
        var cast = try decoder.decode([Character].self, from: json)
        return cast
    } catch {
        print("Error")
    }
    return []
}

let cast = doDecode()

print("# Cs \(cast.count)")

当我尝试添加辅助函数时,

extension Character {
    private func tryDecode<T: Codable>(_ data: KeyedDecodingContainer<CodingKeys>, type: T.Type, forKey: CodingKeys, empty: T) -> T {
        do {
            let result = try data.decode(T.self, forKey: forKey)
            return result
        } catch { return empty }
    }
}

我得到一个编译器错误:

初始化前使用的变量“self.firstName”

当我从初始化程序中调用它时:

init(from decoder: Decoder) throws {
    let data = try decoder.container(keyedBy: CodingKeys.self)
    firstName = tryDecode(data, type: String.self, forKey: Character.CodingKeys.firstName, empty: "")
    lastName = tryDecode(data, type: String.self, forKey: Character.CodingKeys.lastName, empty: "")
}

我输入了“forKey:”,有和没有完整路径。同样的结果。

我做错了什么?为什么编译器不知道“forKey:”参数指的是枚举,而不是 self.firstName?

我在 Xcode playground 中运行最新的 Xcode 和 macOS 版本。

swift codable
1个回答
0
投票

与您的问题无关,但 Character 是原生的 Swift 类型。您应该为自定义结构选择另一个名称,以避免在引用本机类型时添加 Swift 前缀。

关于实际问题,您已将方法声明为实例方法,因此在尝试调用它之前需要初始化所有属性。考虑到您的方法不需要访问 self,您可以将方法声明为静态的,以避免必须初始化结构的所有属性。调用您的方法时,您需要添加结构前缀/.

Char.tryDecode(...

struct Char: Codable {
    var firstName: String
    var lastName: String
    
     enum CodingKeys: String, CodingKey {
        case
            firstName,
            lastName
    }
 
    init(from decoder: Decoder) throws {
        let data = try decoder.container(keyedBy: CodingKeys.self)
        firstName = Char.tryDecode(data, type: String.self, forKey: Char.CodingKeys.firstName, empty: "")
        lastName = Char.tryDecode(data, type: String.self, forKey: Char.CodingKeys.lastName, empty: "")
    }
}

extension Char {
    private static func tryDecode<T: Codable>(_ data: KeyedDecodingContainer<CodingKeys>, type: T.Type, forKey: CodingKeys, empty: T) -> T {
        do {
            return try data.decode(T.self, forKey: forKey)
        } catch {
            return empty
        }
    }
}

let json = Data("""
[
    {
        "firstName": "Jon",
        "lastName": "Dough"
   },
    {
        "firstName": "Jane",
        "lastName": "Dough"
    }
]
""".utf8)

let decoder = JSONDecoder()
func doDecode() -> [Char] {
    do {
        return try decoder.decode([Char].self, from: json)
    } catch {
        print("Error")
    }
    return []
}

let cast = doDecode()

print("# Cs \(cast.count)")
© www.soinside.com 2019 - 2024. All rights reserved.