【问题标题】:How to get any type array field from alamofire response?如何从 alamofire 响应中获取任何类型的数组字段?
【发布时间】:2021-01-12 05:00:57
【问题描述】:

我的 json 响应中有这样的字段:

"title": [2402, "Dr.", "Prof.", "Prof. Dr.", "HM"]

我想解析它。我有我的模型课:

struct AppDataModel:Decodable {
    ...
    let title = Dictionary<String,Any>()
    
    
    enum CodingKeys: String,CodingKey{
        case title
        ...
    }
    ...
}

如您所见,我尝试使用Dictionary&lt;String,Any&gt;()。而且我还考虑了 Any -> [Any] 的数组,但我通常会收到这样的错误:

Type 'AppDataModel' does not conform to protocol 'Decodable'

我认为我必须像处理普通的json一样处理它。但是我在 Swift 中没有找到这样的数据类型,只有字典。那么,也许有人知道如何处理这些响应字段?

【问题讨论】:

  • 除非您在代码中解码并正确处理不同的类型,否则您无法解码异构数组。
  • @vadian,那么我该如何解决这个问题呢?我考虑过 json 处理,但正如我所见,这是不可能的
  • 有多种方法,解决方案取决于您的需求。例如,前导整数是否相关?
  • 这个整数是这些字符串的翻译字符串id,所以可以更改。至于我的需要 - 我只需要获取没有整数的字符串数据
  • 实现init(from decoder,将title解码为unkeyedContainer,取出值,删除第一个并将其余的分配给[String]类型

标签: swift alamofire decodable


【解决方案1】:

这是一个将单个键解码为异构数组的示例

let jsonString = """
{"title": [2402, "Dr.", "Prof.", "Prof. Dr.", "HM"], "name":"Foo"}
"""

struct AppDataModel : Decodable {
    let titles : [String]
    let name : String
    
    private enum CodingKeys: String, CodingKey { case title, name }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        var titlesContainer = try container.nestedUnkeyedContainer(forKey: .title)
        var titleArray = [String]()
        let _ = try titlesContainer.decode(Int.self) // decode and drop the leading integer
        while !titlesContainer.isAtEnd { // decode all following strings
            titleArray.append(try titlesContainer.decode(String.self))
        }
        titles = titleArray
        self.name = try container.decode(String.self, forKey: .name)
    }
}

let data = Data(jsonString.utf8)

do {
    let result = try JSONDecoder().decode(AppDataModel.self, from: data)
    print(result)
} catch {
    print(error)
}

【讨论】:

  • 在我的初始化范围内我看到这样的错误Return from initializer without initializing all stored properties,我必须向你展示什么,也许你会帮助我?
  • 可能与我在这个模型中还有一些其他键有关?
  • 我的示例只包含一个键。如果还有更多,则必须在单独的行中解码每个键。我用第二个键更新了答案。
【解决方案2】:
struct AppDataModel: Codable {
    let title: [Title]?
}

extension AppDataModel {
    init(data: Data) throws {
        self = try newJSONDecoder().decode(AppDataModel.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func with(
        title: [Title]?? = nil
    ) -> AppDataModel {
        return AppDataModel(
            title: title ?? self.title
        )
    }

    func jsonData() throws -> Data {
        return try newJSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

enum Title: Codable {
    case integer(Int)
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Int.self) {
            self = .integer(x)
            return
        }
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        throw DecodingError.typeMismatch(Title.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Title"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .integer(let x):
            try container.encode(x)
        case .string(let x):
            try container.encode(x)
        }
    }
}

func newJSONDecoder() -> JSONDecoder {
    let decoder = JSONDecoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        decoder.dateDecodingStrategy = .iso8601
    }
    return decoder
}

func newJSONEncoder() -> JSONEncoder {
    let encoder = JSONEncoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        encoder.dateEncodingStrategy = .iso8601
    }
    return encoder
}

//use

do {
    let appDataModel = try AppDataModel(json)
}
catch{
    //handle error
}

【讨论】:

    猜你喜欢
    • 2018-12-11
    • 2020-08-12
    • 1970-01-01
    • 1970-01-01
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    • 2016-03-23
    • 2016-05-29
    相关资源
    最近更新 更多