/ / Swift 4 JSONデコード可能なデコード型の簡単な方法 - json、swift、swift4、codable

Swift 4 JSONデコード可能なデコード型の簡単な方法 - json、swift、swift4、codable

swift4のコーディング可能なプロトコルでは、フードデータとデータ変換戦略の下で大きなレベルがあります。

JSON:

{
"name": "Bob",
"age": 25,
"tax_rate": "4.25"
}

私はそれを次の構造に強要した​​い

struct ExampleJson: Decodable {
var name: String
var age: Int
var taxRate: Float

enum CodingKeys: String, CodingKey {
case name, age
case taxRate = "tax_rate"
}
}

日付デコードストラテジーは、ストリングベースの日付を日付に変換することができる。

StringをベースにしたFloatでこれを行うものがありますか?

それ以外の場合は、CodingKeyを使用して文字列を取り込み、コンピューティングget:

    enum CodingKeys: String, CodingKey {
case name, age
case sTaxRate = "tax_rate"
}
var sTaxRate: String
var taxRate: Float { return Float(sTaxRate) ?? 0.0 }

このようなストランドは、必要以上にメンテナンスをしているようです。

これは最もシンプルな方法ですか、他の型変換のためのDateDecodingStrategyに類似したものがありますか?

更新:私は注意する必要があります:私はまた、上書きのルートを行った

init(from decoder:Decoder)

しかし、それは反対の方向にあります。私はそれを自分のためにすべてやります。

回答:

回答№1の13

残念ながら、私はそのようなオプションが現在の 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)
}
}

次に、このタイプのプロパティを持つことができ、自動生成された 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"
}
}

残念ながら、今あなたは 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"
}
}

これはまだ本当にすべきではありませんが、将来のバージョンの JSONDecoder APIには、より多くのカスタムのデコードオプションが含まれています。そうでなければ、 Codable API自体

しかし、ラッパータイプを作成する利点の1つは、手動デコードとエンコーディングをより簡単にするためにも使用できることです。たとえば、手動デコードの場合:

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
}
}

回答№2の10

いつでも手動でデコードすることができます。だから、与えられた:

{
"name": "Bob",
"age": 25,
"tax_rate": "4.25"
}

できるよ:

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"
}
}

見る 手動でエンコードとデコードエンコードとデコードのカスタムタイプ.

しかし、私はそれに相当するよりエレガントな文字列変換プロセスがあるように思えます DateDecodingStrategy いくつのJSONソースが間違って数値を文字列として返すかを考えると、


回答№3の6

ニーズに応じて、問題を解決するために次の2つの方法のいずれかを選択できます。


#1。使用 Decodable init(from:) イニシャライザ

あなたがから変換する必要があるときにこの戦略を使用してください StringFloat 単一の構造体、列挙型またはクラスの場合

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
}

}

使用法:

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
*/

#2。中間モデルを使用する

JSONに多数のネストされたキーがある場合、または複数のキー(たとえば、 StringFloat)をJSONから取得します。

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
}

}

使用法:

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
*/

回答№4の場合は1

あなたは使うことができます lazy var プロパティを別の型に変換する:

struct ExampleJson: Decodable {
var name: String
var age: Int
lazy var taxRate: Float = {
Float(self.tax_rate)!
}()

private var tax_rate: String
}

このアプローチの1つの欠点は、 let アクセスしたい場合は定数 taxRate最初にアクセスしたときから、構造体に突然変異を起こしています。

// Cannot use `let` here
var example = try! JSONDecoder().decode(ExampleJson.self, from: data)

回答№5の場合は1

私はこれが本当に遅い答えであることを知っていますが、私は Codable 数日後にのみ。そして私は同様の問題にぶつかった。

文字列を浮動小数点数に変換するには、 KeyedDecodingContainer 拡張の中のメソッドをから呼び出す init(from decoder: Decoder){}

この問題で言及されている問題については、以下に書いた拡張を参照してください。

extension KeyedDecodingContainer {

func decodeIfPresent(_ type: Float.Type, forKey key: K, transformFrom: String.Type) throws -> Float? {

guard let value = try decodeIfPresent(transformFrom, forKey: key) else {
return nil
}
return Float(value)
}

func decode(_ type: Float.Type, forKey key: K, transformFrom: String.Type) throws -> Float? {

return Float(try decode(transformFrom, forKey: key))
}
}

あなたはこのメソッドをから呼び出すことができます init(from decoder: Decoder) 方法。以下の例を参照してください。

init(from decoder: Decoder) throws {

let container = try decoder.container(keyedBy: CodingKeys.self)

taxRate = try container.decodeIfPresent(Float.self, forKey: .taxRate, transformFrom: String.self)
}

実際には、このアプローチを使用して、任意のタイプのデータを他のタイプに変換することができます。あなたは string to Date, string to bool, string to float, float to int

実際に文字列をDateオブジェクトに変換するには、このアプローチを JSONEncoder().dateEncodingStrategy これを正しく記述すると、同じ応答に異なる日付形式を含めることができるからです。

私は助けてくれるといいですね。


答え№6の場合は-1

ここにリンクの説明を入力してくださいSwift4でJSONDecodableを使用する方法
1)JSONレスポンスを取得し、構造体を作成する 2)構造体のDecodableクラスを準拠させる 3)プロジェクト(簡易例)のその他のステップ