/ / स्विफ्ट 4 जेएसओएन डीकोड प्रकार बदलने के लिए सबसे आसान तरीका - जेसन, स्विफ्ट, स्विफ्ट 4, कोडेबल

स्विफ्ट 4 जेएसओएन डीकोड टाइप करने के लिए डीकोडेबल सबसे आसान तरीका - जेसन, स्विफ्ट, स्विफ्ट 4, कोडेबल

स्विफ्ट 4 के कोडेबल प्रोटोकॉल के साथ हुड तिथि और डेटा रूपांतरण रणनीतियों के तहत एक महान स्तर है।

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

डेट डिकोडिंग रणनीति एक स्ट्रिंग आधारित दिनांक को एक तिथि में परिवर्तित कर सकती है।

क्या ऐसा कुछ है जो स्ट्रिंग आधारित फ्लोट के साथ करता है

अन्यथा मैं स्ट्रिंग लाने और कंप्यूटिंग प्राप्त करने के लिए कोडिंगकी का उपयोग करके अटक गया हूं:

    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 एपीआई। क्रम में केवल एक विकल्प मौजूद है बदलना असाधारण फ़्लोटिंग-पॉइंट मान एक स्ट्रिंग प्रतिनिधित्व से और करने के लिए।

मैन्युअल रूप से डीकोड करने का एक और संभावित समाधान एक परिभाषित करना है 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 एपीआई में अधिक कस्टम डिकोडिंग विकल्प शामिल होंगे, या अन्य में टाइप रूपांतरणों को व्यक्त करने की क्षमता होगी Codable एपीआई स्वयं

हालांकि रैपर प्रकार बनाने का एक फायदा यह है कि मैन्युअल डिकोडिंग और एन्कोडिंग को सरल बनाने के लिए इसका उपयोग भी किया जा सकता है। उदाहरण के लिए, मैन्युअल डिकोडिंग के साथ:

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

जवाब के लिए 10 № 2

आप हमेशा मैन्युअल रूप से डीकोड कर सकते हैं। तो, दिया गया:

{
"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 स्रोत वहां गलत संख्याओं को स्ट्रिंग के रूप में वापस कर देते हैं।


जवाब के लिए 6 № 3

आपकी जरूरतों के अनुसार, आप अपनी समस्या को हल करने के लिए निम्न में से दो तरीकों में से एक चुन सकते हैं।


# 1। का उपयोग करते हुए Decodable init(from:) प्रारंभकर्ता

जब आपको कन्वर्ट करने की आवश्यकता होती है तो इस रणनीति का प्रयोग करें String सेवा मेरे Float एक ही संरचना, enum या वर्ग के लिए।

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 में कई नेस्टेड कुंजी हों या जब आपको कई कुंजियों को परिवर्तित करने की आवश्यकता हो (उदा String सेवा मेरे Float) अपने 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
*/

उत्तर के लिए 1 № 4

आप उपयोग कर सकते हैं lazy var संपत्ति को दूसरे प्रकार में बदलने के लिए:

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

private var tax_rate: String
}

इस दृष्टिकोण का एक नुकसान यह है कि आप एक परिभाषित नहीं कर सकते हैं let अगर आप पहुंचना चाहते हैं तो निरंतर taxRate, पहली बार जब आप इसे एक्सेस करते हैं, तो आप संरचना को म्यूट कर रहे हैं।

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

उत्तर के लिए 1 № 5

मुझे पता है कि यह वास्तव में देर से जवाब है, लेकिन मैंने काम करना शुरू कर दिया 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 आदि।

वास्तव में एक स्ट्रिंग को डेट ऑब्जेक्ट में कनवर्ट करने के लिए, मैं इस दृष्टिकोण को पसंद करूंगा JSONEncoder().dateEncodingStrategy क्योंकि यदि आप इसे सही तरीके से लिखते हैं, तो आप एक ही प्रतिक्रिया में अलग-अलग दिनांक स्वरूप शामिल कर सकते हैं।

उम्मीद है कि मैंने मदद की।


उत्तर के लिए -1 № 6

यहां लिंक विवरण दर्ज करेंSwift4 में JSONDecodable का उपयोग कैसे करें
1) जेएसओएन प्रतिक्रिया प्राप्त करें और संरचना बनाएं 2) संरचना में डीकोडेबल वर्ग अनुरूप 3) परियोजना का पालन करने में अन्य कदम (सरल उदाहरण)