This is my json:
"latest_receipt_info" = (
{
"expires_date" = "2022-02-15 07:33:27 Etc/GMT";
"is_in_intro_offer_period" = false;
"is_trial_period" = false;
"product_id" = "com.apollo66.InAppPurchasePT.AutoRenewableGroup.ARenewable6";
"transaction_id" = 1000000968989231;
"web_order_line_item_id" = 1000000072680194;
},
{
"expires_date" = "2022-02-15 07:33:27 Etc/GMT";
"is_in_intro_offer_period" = false;
"is_trial_period" = false;
"product_id" = "com.apollo66.InAppPurchasePT.AutoRenewableGroup.ARenewable6";
"transaction_id" = 1000000968989231;
"web_order_line_item_id" = 1000000072680194;
"cancellation_date" = "2019-12-05 19:14:48 Etc/GMT",
}
);
As you can see there are 1 extra attributes in the 1st dictionary. Now I want to make an object for this kind of dictionary where sometimes "cancellation_date": "***" attribute could appear and sometimes not. How can I do it?
This is my object:
struct LatestReceiptInfo {
var expiresDate: Date? = nil
let isInIntroOfferPeriod: Bool
let isTrialPeriod: Bool
let productId : String
var cancellationDate: Date? = nil
}
And this is how I'm trying to bind the values:
for receiptInf in receiptInfo {
let recInf = receiptInf as! NSDictionary
guard let expiresDate = recInf["expires_date"] as? Date,
let isInIntroOfferPeriod = recInf["is_in_intro_offer_period"] as? Bool,
let isTrialPeriod = recInf["is_trial_period"] as? Bool,
let productId = recInf["product_id"] as? String else {
print("Something is not well")
continue
}
let latestReceiptInfo = LatestReceiptInfo(expiresDate: expiresDate, isInIntroOfferPeriod: isInIntroOfferPeriod, isTrialPeriod: isTrialPeriod, productId: productId, cancellationDate: nil)
latestReceiptInfoArray.append(latestReceiptInfo)
}
But it's not working. Thanks!
CodePudding user response:
Here is an example with notes
1- Any json should be surrounded with {}
or []
and yours isn't
2- You have an invalid json
"product_id" = "com.com.ARenewable6"; // = should be : and ; should be ,
3- You make a bool a string with "false" while it should be false
4- For supporting properties without _ you need to set keyDecodingStrategy
to .convertFromSnakeCase
and
5- For supporting string dates you need to assign a formatter to dateDecodingStrategy
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let str = """
{
"latest_receipt_info": [
{
"expires_date": "2019-12-10 17:37:05 Etc/GMT",
"is_in_intro_offer_period": false,
"is_trial_period": false,
"product_id":"com.com.ARenewable6",
"cancellation_date": "2019-12-05 19:14:48 Etc/GMT"
}
]}
"""
do {
// let data = try JSONSerialization.data(withJSONObject: receiptInfo, options: [:])
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let form = DateFormatter()
form.dateFormat = "yyyy-MM-dd HH:mm:ss VV"
decoder.dateDecodingStrategy = .formatted(form)
let res = try decoder.decode([String:[LatestReceiptInfo]].self, from: Data(str.utf8))
print(res)
}
catch {
print(error)
}
}
}
struct LatestReceiptInfo: Codable {
var expiresDate: Date? = nil
let isInIntroOfferPeriod: Bool
let isTrialPeriod: Bool
let productId : String
var cancellationDate: Date? = nil
}
CodePudding user response:
I have made stack.json file and add it to our project file by dragging it to file project
{
"latest_receipt_info": [
{
"expires_date": "2019-12-10 17:37:05 Etc/GMT",
"is_in_intro_offer_period": false,
"is_trial_period": false,
"product_id": "com.com.ARenewable6",
"cancellation_date": "2019-12-05 19:14:48 Etc/GMT"
},
{
"expires_date": "2019-12-10 17:37:05 Etc/GMT",
"is_in_intro_offer_period": false,
"is_trial_period": false,
"product_id": "com.com.ARenewable6"
}
]
}
for simplicity, I m naming variable as the same described in the JSON, though you can choose your own name for your modal class
struct JSONModal : Codable {
var latest_receipt_info : [LatestReceiptInfo]
}
struct LatestReceiptInfo: Codable {
var expires_date: Date? = nil
let is_in_intro_offer_period: Bool
let is_trial_period: Bool
let product_id : String
var cancellation_date: Date? = nil
}
1-> now first we want to load this json file from our project in real life project you will load it from web or some API call
guard let jsonUrl = Bundle.main.url(forResource: "stack", withExtension: "json") else {
fatalError("sorry this file doesnt exist")
}
guard let data = try? Data(contentsOf: jsonUrl) else {
fatalError("json url may be currupted I m unable to produce Data out of url")
}
at this point, you have a Data object with JSON store in it its time to decode it
let jsonDecoder = JSONDecoder()
let form = DateFormatter()
form.dateFormat = "yyyy-MM-dd HH:mm:ss VV" //thanks sh_khan for this
jsonDecoder.dateDecodingStrategy = .formatted(form)
guard let json = try? jsonDecoder.decode(JSONModal.self, from: data) else {
fatalError("sorry unable to get json from given data may happen the model yiu have creatd have some problem")
}
now when decode will not find any data like in our case one of the entries doesn't have a cancellation date then it will give nil
so we have our JSON lets print it
print("json is \(json)")