如何在 Swift 中解码类型为 JSON 字典的 属性?
How to decode a property with type of JSON dictionary in Swift?
我目前在测试中遇到以下错误:
如果我正确理解了问题,那么我需要将我的字典转换为解码器。
目前我有下一个代码:
static var validConfirmAuthorizationData: [String: Any] { return json("valid_confirm_authorization_data") }
具有以下结构:
{
"data": {
"id": "1",
"success": true
}
}
以及我与解码器一起使用的响应 class 本身:
public struct SEConfirmAuthorizationResponse: Decodable {
public let id: String
public let success: Bool
enum CodingKeys: String, CodingKey {
case data
}
enum DataCodingKeys: String, CodingKey {
case id
case success
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let dataContainer = try container.nestedContainer(keyedBy: DataCodingKeys.self, forKey: .data)
id = try dataContainer.decode(String.self, forKey: .id)
success = try dataContainer.decode(Bool.self, forKey: .success)
}
}
您有几个选择,您可以创建一个带有名为数据的变量的可编码结构。数据将具有 SEConfirmAuthorizationResponse 类型。然后你会解码成新的结构。
您可以像这样解码成字典:
let decoded = JsonDecoder().decode([String: SEConfirmAuthorizationResponse].self, from: someData)
let response = decoded[“data”]
或者您可以编写一个我通常尽量避免的自定义解码器。
原来是这样解决问题的
首先,我创建了SpecDecodableModel
:
public struct SpecDecodableModel<T: Decodable> {
static func create(from fixture: [String: Any]) -> T {
let decoder = JSONDecoder()
decoder.dateDecodingStrategyFormatters = [DateUtils.dateFormatter, DateUtils.ymdDateFormatter]
let fixtureData = Data(fixture.jsonString!.utf8)
return try! decoder.decode(T.self, from: fixtureData)
}
}
之后,我可以将 JSON
结构转换为所需的类型。
现在回到最初的问题,我可以按如下方式解决:
let fixture = DataFixtures.validConfirmAuthorizationData
let response = SpecDecodableModel<SEConfirmAuthorizationResponse>.create(from: fixture)
更新:
public extension Dictionary {
var jsonString: String? {
if let data = try? JSONSerialization.data(withJSONObject: self, options: []),
let string = String(data: data, encoding: String.Encoding.utf8) {
return string
}
return nil
}
}
public struct AuthorizationData: Codable {
public let id: String
public let success: Bool
init(id: String, success: Bool) {
self.id = id
self.success = success
}
enum CodingKeys: String, CodingKey {
case id
case success
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
success = try container.decode(Bool.self, forKey: .success)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(success, forKey: .success)
}
}
public struct SEConfirmAuthorizationResponse: Codable {
public let data: AuthorizationData
enum CodingKeys: String, CodingKey {
case data
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
data = try container.decode(AuthorizationData.self, forKey: .data)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(data, forKey: .data)
}
init(_ data: AuthorizationData) {
self.data = data
}
}
func authData() -> Data? {
let authorizationData = AuthorizationData(id: "1", success: true)
let response = SEConfirmAuthorizationResponse(authorizationData)
guard let prettyJsonData = try? JSONEncoder().encode(response) else {
return nil
}
if let jsonString = String(data: prettyJsonData, encoding: .utf8) {
print("jsonString", jsonString)
}
return prettyJsonData
}
func authResponse() {
if let data = authData() {
guard let response = try? JSONDecoder().decode(SEConfirmAuthorizationResponse.self, from: data) else {
return
}
print("response", response)
}
}
我目前在测试中遇到以下错误:
如果我正确理解了问题,那么我需要将我的字典转换为解码器。
目前我有下一个代码:
static var validConfirmAuthorizationData: [String: Any] { return json("valid_confirm_authorization_data") }
具有以下结构:
{
"data": {
"id": "1",
"success": true
}
}
以及我与解码器一起使用的响应 class 本身:
public struct SEConfirmAuthorizationResponse: Decodable {
public let id: String
public let success: Bool
enum CodingKeys: String, CodingKey {
case data
}
enum DataCodingKeys: String, CodingKey {
case id
case success
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let dataContainer = try container.nestedContainer(keyedBy: DataCodingKeys.self, forKey: .data)
id = try dataContainer.decode(String.self, forKey: .id)
success = try dataContainer.decode(Bool.self, forKey: .success)
}
}
您有几个选择,您可以创建一个带有名为数据的变量的可编码结构。数据将具有 SEConfirmAuthorizationResponse 类型。然后你会解码成新的结构。
您可以像这样解码成字典:
let decoded = JsonDecoder().decode([String: SEConfirmAuthorizationResponse].self, from: someData)
let response = decoded[“data”]
或者您可以编写一个我通常尽量避免的自定义解码器。
原来是这样解决问题的
首先,我创建了SpecDecodableModel
:
public struct SpecDecodableModel<T: Decodable> {
static func create(from fixture: [String: Any]) -> T {
let decoder = JSONDecoder()
decoder.dateDecodingStrategyFormatters = [DateUtils.dateFormatter, DateUtils.ymdDateFormatter]
let fixtureData = Data(fixture.jsonString!.utf8)
return try! decoder.decode(T.self, from: fixtureData)
}
}
之后,我可以将 JSON
结构转换为所需的类型。
现在回到最初的问题,我可以按如下方式解决:
let fixture = DataFixtures.validConfirmAuthorizationData
let response = SpecDecodableModel<SEConfirmAuthorizationResponse>.create(from: fixture)
更新:
public extension Dictionary {
var jsonString: String? {
if let data = try? JSONSerialization.data(withJSONObject: self, options: []),
let string = String(data: data, encoding: String.Encoding.utf8) {
return string
}
return nil
}
}
public struct AuthorizationData: Codable {
public let id: String
public let success: Bool
init(id: String, success: Bool) {
self.id = id
self.success = success
}
enum CodingKeys: String, CodingKey {
case id
case success
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
success = try container.decode(Bool.self, forKey: .success)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(success, forKey: .success)
}
}
public struct SEConfirmAuthorizationResponse: Codable {
public let data: AuthorizationData
enum CodingKeys: String, CodingKey {
case data
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
data = try container.decode(AuthorizationData.self, forKey: .data)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(data, forKey: .data)
}
init(_ data: AuthorizationData) {
self.data = data
}
}
func authData() -> Data? {
let authorizationData = AuthorizationData(id: "1", success: true)
let response = SEConfirmAuthorizationResponse(authorizationData)
guard let prettyJsonData = try? JSONEncoder().encode(response) else {
return nil
}
if let jsonString = String(data: prettyJsonData, encoding: .utf8) {
print("jsonString", jsonString)
}
return prettyJsonData
}
func authResponse() {
if let data = authData() {
guard let response = try? JSONDecoder().decode(SEConfirmAuthorizationResponse.self, from: data) else {
return
}
print("response", response)
}
}