Dru*_*man 24 json swift swift4 codable
使用swift4的Codable协议,有很多级别的引擎日期和数据转换策略.
鉴于JSON:
{
"name": "Bob",
"age": 25,
"tax_rate": "4.25"
}
Run Code Online (Sandbox Code Playgroud)
我想将它强制进入以下结构
struct ExampleJson: Decodable {
var name: String
var age: Int
var taxRate: Float
enum CodingKeys: String, CodingKey {
case name, age
case taxRate = "tax_rate"
}
}
Run Code Online (Sandbox Code Playgroud)
日期解码策略可以将基于字符串的日期转换为日期.
基于String的Float是否有某种功能
否则我一直坚持使用CodingKey引入一个String并使用计算得到:
enum CodingKeys: String, CodingKey {
case name, age
case sTaxRate = "tax_rate"
}
var sTaxRate: String
var taxRate: Float { return Float(sTaxRate) ?? 0.0 }
Run Code Online (Sandbox Code Playgroud)
这种方式比我看起来需要更多的维护.
这是最简单的方式还是类似于DateDecodingStrategy用于其他类型的转换?
更新:我应该注意:我也走了超越的路线
init(from decoder:Decoder)
Run Code Online (Sandbox Code Playgroud)
但这是相反的方向,因为它迫使我为自己做这一切.
Ham*_*ish 18
不幸的是,我不相信当前的JSONDecoder
API中存在这样的选项.只有一个选项才能将异常浮点值转换为字符串表示形式.
手动解码的另一种可能的解决方案是Codable
为任何LosslessStringConvertible
可以编码和解码其String
表示的任何人定义包装器类型:
struct StringCodableMap<Decoded : LosslessStringConvertible> : Codable {
var decoded: Decoded
init(_ decoded: Decoded) {
self.decoded = decoded
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let decodedString = try container.decode(String.self)
guard let decoded = Decoded(decodedString) else {
throw DecodingError.dataCorruptedError(
in: container, debugDescription: """
The string \(decodedString) is not representable as a \(Decoded.self)
"""
)
}
self.decoded = decoded
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(decoded.description)
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以拥有这种类型的属性并使用自动生成的Codable
一致性:
struct Example : Codable {
var name: String
var age: Int
var taxRate: StringCodableMap<Float>
private enum CodingKeys: String, CodingKey {
case name, age
case taxRate = "tax_rate"
}
}
Run Code Online (Sandbox Code Playgroud)
虽然不幸的是,现在你必须谈论taxRate.decoded
以便与Float
价值互动.
但是,您总是可以定义一个简单的转发计算属性,以减轻这种情况:
struct Example : Codable {
var name: String
var age: Int
private var _taxRate: StringCodableMap<Float>
var taxRate: Float {
get { return _taxRate.decoded }
set { _taxRate.decoded = newValue }
}
private enum CodingKeys: String, CodingKey {
case name, age
case _taxRate = "tax_rate"
}
}
Run Code Online (Sandbox Code Playgroud)
虽然这仍然不像它应该的那样光滑 - 希望JSONDecoder
API 的更高版本将包括更多自定义解码选项,或者能够在Codable
API本身内表达类型转换.
然而,创建包装器类型的一个优点是它也可以用于使手动解码和编码更简单.例如,使用手动解码:
struct Example : Decodable {
var name: String
var age: Int
var taxRate: Float
private enum CodingKeys: String, CodingKey {
case name, age
case taxRate = "tax_rate"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.age = try container.decode(Int.self, forKey: .age)
self.taxRate = try container.decode(StringCodableMap<Float>.self,
forKey: .taxRate).decoded
}
}
Run Code Online (Sandbox Code Playgroud)
Rob*_*Rob 14
您始终可以手动解码.所以,给定:
{
"name": "Bob",
"age": 25,
"tax_rate": "4.25"
}
Run Code Online (Sandbox Code Playgroud)
你可以做:
struct Example: Codable {
let name: String
let age: Int
let taxRate: Float
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
age = try values.decode(Int.self, forKey: .age)
guard let rate = try Float(values.decode(String.self, forKey: .taxRate)) else {
throw DecodingError.dataCorrupted(.init(codingPath: [CodingKeys.taxRate], debugDescription: "Expecting string representation of Float"))
}
taxRate = rate
}
enum CodingKeys: String, CodingKey {
case name, age
case taxRate = "tax_rate"
}
}
Run Code Online (Sandbox Code Playgroud)
见编码和解码手动的编码和解码自定义类型.
但我同意,似乎应该有一个更优雅的字符串转换过程相当于DateDecodingStrategy
给出了多少个JSON源,错误地将数值作为字符串返回.
根据您的需要,您可以选择以下两种方法之一来解决您的问题.
Decodable
init(from:)
初始化程序当你需要从转换使用此策略String
来Float
为单一结构,枚举或类.
import Foundation
struct ExampleJson: Decodable {
var name: String
var age: Int
var taxRate: Float
enum CodingKeys: String, CodingKey {
case name, age, taxRate = "tax_rate"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: CodingKeys.name)
age = try container.decode(Int.self, forKey: CodingKeys.age)
let taxRateString = try container.decode(String.self, forKey: CodingKeys.taxRate)
guard let taxRateFloat = Float(taxRateString) else {
let context = DecodingError.Context(codingPath: container.codingPath + [CodingKeys.taxRate], debugDescription: "Could not parse json key to a Float object")
throw DecodingError.dataCorrupted(context)
}
taxRate = taxRateFloat
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
import Foundation
let jsonString = """
{
"name": "Bob",
"age": 25,
"tax_rate": "4.25"
}
"""
let data = jsonString.data(using: String.Encoding.utf8)!
let decoder = JSONDecoder()
let exampleJson = try! decoder.decode(ExampleJson.self, from: data)
dump(exampleJson)
/*
prints:
? __lldb_expr_126.ExampleJson
- name: "Bob"
- age: 25
- taxRate: 4.25
*/
Run Code Online (Sandbox Code Playgroud)
如果JSON中有许多嵌套键,或者需要从JSON 转换许多键(例如从中转换String
为Float
),请使用此策略.
import Foundation
fileprivate struct PrivateExampleJson: Decodable {
var name: String
var age: Int
var taxRate: String
enum CodingKeys: String, CodingKey {
case name, age, taxRate = "tax_rate"
}
}
struct ExampleJson: Decodable {
var name: String
var age: Int
var taxRate: Float
init(from decoder: Decoder) throws {
let privateExampleJson = try PrivateExampleJson(from: decoder)
name = privateExampleJson.name
age = privateExampleJson.age
guard let convertedTaxRate = Float(privateExampleJson.taxRate) else {
let context = DecodingError.Context(codingPath: [], debugDescription: "Could not parse json key to a Float object")
throw DecodingError.dataCorrupted(context)
}
taxRate = convertedTaxRate
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
import Foundation
let jsonString = """
{
"name": "Bob",
"age": 25,
"tax_rate": "4.25"
}
"""
let data = jsonString.data(using: String.Encoding.utf8)!
let decoder = JSONDecoder()
let exampleJson = try! decoder.decode(ExampleJson.self, from: data)
dump(exampleJson)
/*
prints:
? __lldb_expr_126.ExampleJson
- name: "Bob"
- age: 25
- taxRate: 4.25
*/
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
14374 次 |
最近记录: |