gpt4 book ai didi

swift - 现场级自定义解码器

转载 作者:搜寻专家 更新时间:2023-11-01 06:28:17 25 4
gpt4 key购买 nike

我正在尝试实现字段级自定义解码,以便可以提供解码器函数来映射值。这最初是为了解决自动将“Y”和“N”的字符串值转换为 true/false 的问题。有没有更简洁的方法可以做到这一点?

这旨在用于相当大的记录的单个字段......但有点失控。

主要目标是不必手动实现每个单独的解码记录中的字段,但要枚举它们并将默认解码器的结果用于没有自定义解码器(可能不应该称为“解码器”)的任何内容。

当前尝试如下所示:

class Foo: Decodable {
var bar: String
var baz: String

init(foo: String) {
self.bar = foo
self.baz = ""
}

enum CodingKeys: String, CodingKey {
case bar
case baz
}

static func customDecoder(for key: CodingKey) -> ((String) -> Any)? {
switch key {
case CodingKeys.baz: return { return $0 == "meow" ? "foo" : "bar" }
default:
return nil
}
}

required init(from decoder: Decoder) throws {
let values: KeyedDecodingContainer = try decoder.container(keyedBy: CodingKeys.self)

if let cde = Foo.customDecoder(for: CodingKeys.bar) {
self.bar = (try cde(values.decode(String.self, forKey: .bar)) as? String)!
} else {
self.bar = try values.decode(type(of: self.bar), forKey: .bar)
}
if let cde = Foo.customDecoder(for: CodingKeys.baz) {
self.baz = (try cde(values.decode(String.self, forKey: .baz)) as? String)!
} else {
self.baz = try values.decode(type(of: self.baz), forKey: .baz)
}
}
}

使用示例:

func testFoo() {
var foo: Foo?

let jsonData = """
{"bar": "foo", "baz": "meow"}
""".data(using: .utf8)

if let data = jsonData {
foo = try? JSONDecoder().decode(Foo.self, from: data)
if let bar = foo {
XCTAssertEqual(bar.bar, "foo")
} else {
XCTFail("bar is not foo")
}
} else {
XCTFail("Could not coerce string into JSON")
}
}

最佳答案

例如我们有一个例子json:

let json = """
{
"id": 1,
"title": "Title",
"thumbnail": "https://www.sample-videos.com/img/Sample-jpg-image-500kb.jpg",
"date": "2014-07-15"
}
""".data(using: .utf8)!

如果我们想解析它,我们可以使用 Codable 协议(protocol)和简单的 NewsCodable 结构:

public struct NewsCodable: Codable {
public let id: Int
public let title: String
public let thumbnail: PercentEncodedUrl
public let date: MyDate
}

PercentEncodedUrl 是我们为 URL 自定义的 Codable 包装器,它将百分比编码添加到 url 字符串。标准 URL 不支持开箱即用。

public struct PercentEncodedUrl: Codable {
public let url: URL

public init(from decoder: Decoder) throws {
let urlString = try decoder.singleValueContainer().decode(String.self)

guard
let encodedUrlString = urlString.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),
let url = URL.init(string: encodedUrlString) else {
throw PercentEncodedUrlError.url(urlString)
}

self.url = url
}

public enum PercentEncodedUrlError: Error {
case url(String)
}
}

如果出于某些奇怪的原因我们需要为日期字符串 (Date decoding has plenty of support in JSONDecoder) 自定义解码器,我们可以提供像 PercentEncodedUrl 这样的包装器。

public struct MyDate: Codable {
public let date: Date

public init(from decoder: Decoder) throws {
let dateString = try decoder.singleValueContainer().decode(String.self)

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
guard let date = dateFormatter.date(from: dateString) else {
throw MyDateError.date(dateString)
}

self.date = date
}

public enum MyDateError: Error {
case date(String)
}
}


let decoder = JSONDecoder()
let news = try! decoder.decode(NewsCodable.self, from: json)

因此我们提供了字段级自定义解码器。

关于swift - 现场级自定义解码器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50978786/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com