Decoding dynamic JSON structure in swift 4 - swift

I have the following issue that I'm not sure how to handle.
My JSON response can look like this:
{
"data": {
"id": 7,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo"
},
"error": null
}
Or like this:
{
"data": [{
"id": 12
}, {
"id": 2
}, {
"id": 5
}, {
"id": 7
}],
"error": null
}
So in short the data can be either a single objet or an Array. What i have is this:
struct ApiData: Decodable {
var data: DataObject?
var error: String?
}
struct DataObject: Decodable {
var userId: Int?
enum CodingKeys: String, CodingKey {
case userId = "id"
}
}
This works fine for the first use case, but it will fail once data turns into
var data: [DataObject?]
How do I make that dynamic without duplicating code?
Edit: This is how i decode the object as well
func makeDataTaskWith(with urlRequest: URLRequest, completion: #escaping(_ apiData: ApiData?) -> ()) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
session.dataTask(with: urlRequest) {
(data, response, error) in
guard let _ = response, let data = data else {return}
if let responseCode = response as? HTTPURLResponse {
print("Response has status code: \(responseCode.statusCode)")
}
do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data)
completion(retreived)
} catch let decodeError as NSError {
print("Decoder error: \(decodeError.localizedDescription)\n")
return
}
}.resume()
}

If data can be a single object or an array write a custom initializer which decodes first an array, if a type mismatch error occurs decode a single object. data is declared as an array anyway.
As token appears only in a single object the property is declared as optional.
struct ApiData: Decodable {
let data : [DataObject]
let error : String?
private enum CodingKeys : String, CodingKey { case data, error }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
data = try container.decode([DataObject].self, forKey: .data)
} catch DecodingError.typeMismatch {
data = [try container.decode(DataObject.self, forKey: .data)]
}
error = try container.decodeIfPresent(String.self, forKey: .error)
}
}
struct DataObject: Decodable {
let userId : Int
let token : String?
private enum CodingKeys: String, CodingKey { case userId = "id", token }
}
Edit: Your code to receive the data can be improved. You should add a better error handling to return also all possible errors:
func makeDataTaskWith(with urlRequest: URLRequest, completion: #escaping(ApiData?, Error?) -> Void) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
session.dataTask(with: urlRequest) {
(data, response, error) in
if let error = error { completion(nil, error); return }
if let responseCode = response as? HTTPURLResponse {
print("Response has status code: \(responseCode.statusCode)")
}
do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
completion(retreived, nil)
} catch {
print("Decoder error: ", error)
completion(nil, error)
}
}.resume()
}

Using power of generic, it simple like below:
struct ApiData<T: Decodable>: Decodable {
var data: T?
var error: String?
}
struct DataObject: Decodable {
private var id: Int?
var userId:Int? {
return id
}
}
Use
if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
//Do somthing
} else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
// Do somthing
}

If you have only two possible outcomes for your data, an option would be to try and parse data to one of the expected types, if that fails you know that the data is of other type and you can then handle it accordingly.
See this

You can try
struct Root: Codable {
let data: DataUnion
let error: String?
}
enum DataUnion: Codable {
case dataClass(DataClass)
case datumArray([Datum])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode([Datum].self) {
self = .datumArray(x)
return
}
if let x = try? container.decode(DataClass.self) {
self = .dataClass(x)
return
}
throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .dataClass(let x):
try container.encode(x)
case .datumArray(let x):
try container.encode(x)
}
}
}
struct Datum: Codable {
let id: Int
}
struct DataClass: Codable {
let id: Int
let token: String
}
let res = try? JSONDecoder().decode(Root.self, from:data)

Related

How to get any type array field from alamofire response?

I have such field in my json response:
"title": [2402, "Dr.", "Prof.", "Prof. Dr.", "HM"]
And I would like to parse it. I have my model class:
struct AppDataModel:Decodable {
...
let title = Dictionary<String,Any>()
enum CodingKeys: String,CodingKey{
case title
...
}
...
}
As you can see I tried to use Dictionary<String,Any>() for it. And also I thought about array of Any -> [Any] but I usually get such error:
Type 'AppDataModel' does not conform to protocol 'Decodable'
I think that I have to process it like an ordinary json. But I didn't find such data type in Swift, only dictionary. So, maybe someone knows how to process such response fields?
This is an example to decode a single key as heterogenous array
let jsonString = """
{"title": [2402, "Dr.", "Prof.", "Prof. Dr.", "HM"], "name":"Foo"}
"""
struct AppDataModel : Decodable {
let titles : [String]
let name : String
private enum CodingKeys: String, CodingKey { case title, name }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var titlesContainer = try container.nestedUnkeyedContainer(forKey: .title)
var titleArray = [String]()
let _ = try titlesContainer.decode(Int.self) // decode and drop the leading integer
while !titlesContainer.isAtEnd { // decode all following strings
titleArray.append(try titlesContainer.decode(String.self))
}
titles = titleArray
self.name = try container.decode(String.self, forKey: .name)
}
}
let data = Data(jsonString.utf8)
do {
let result = try JSONDecoder().decode(AppDataModel.self, from: data)
print(result)
} catch {
print(error)
}
struct AppDataModel: Codable {
let title: [Title]?
}
extension AppDataModel {
init(data: Data) throws {
self = try newJSONDecoder().decode(AppDataModel.self, from: data)
}
init(_ json: String, using encoding: String.Encoding = .utf8) throws {
guard let data = json.data(using: encoding) else {
throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
}
try self.init(data: data)
}
init(fromURL url: URL) throws {
try self.init(data: try Data(contentsOf: url))
}
func with(
title: [Title]?? = nil
) -> AppDataModel {
return AppDataModel(
title: title ?? self.title
)
}
func jsonData() throws -> Data {
return try newJSONEncoder().encode(self)
}
func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
return String(data: try self.jsonData(), encoding: encoding)
}
}
enum Title: Codable {
case integer(Int)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Int.self) {
self = .integer(x)
return
}
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
throw DecodingError.typeMismatch(Title.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Title"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .integer(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}
func newJSONDecoder() -> JSONDecoder {
let decoder = JSONDecoder()
if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
decoder.dateDecodingStrategy = .iso8601
}
return decoder
}
func newJSONEncoder() -> JSONEncoder {
let encoder = JSONEncoder()
if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
encoder.dateEncodingStrategy = .iso8601
}
return encoder
}
//use
do {
let appDataModel = try AppDataModel(json)
}
catch{
//handle error
}

How to handle two different JSON responses with Alamofire

I am making a login functionality in a SwiftUI app.
When the login in successful the JSON response is:
{
"user_id": 41,
"token": "Token",
"token_type": "bearer",
"expires_in": 12096000
}
When the login is failed the JSON response is:
{
"message": "this is a failure message"
}
I made two different structs to encode the responses
struct LoginResponseModelFailure:Codable {
let message:String
}
struct LoginResponseModelSuccess:Codable{
let user_id: Int
let token: String
let token_type : String
let expires_in: Int
}
Do I need to merge these two structs to single one? if so how to do that?
How can I handle two different responses using alamofire or urlSession?
To avoid optionals my suggestion is to declare the root object as enum with associated values
enum Response : Decodable {
case success(ResponseSuccess)
case failure(ResponseFailure)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
self = .success(try container.decode(ResponseSuccess.self))
} catch {
self = .failure(try container.decode(ResponseFailure.self))
}
}
}
The other structs can remain as they are except the names became camelCase
struct ResponseFailure : Decodable {
let message : String
}
struct ResponseSuccess : Decodable {
let userId : Int
let token : String
let tokenType : String
let expiresIn : Int
}
To decode the data switch on the result, data represents the received raw data. The key decoding strategy is added to handle the snake_case keys
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Response.self, from: data)
switch result {
case .success(let data): print(data)
case .failure(let error): print(error)
}
} catch {
print(error)
}
As mentioned in the comment, one way is to create one Struct but then you have to make all the properties Optional as below,
struct LoginResponse: Codable {
let user_id: Int?
let token: String?
let token_type : String?
let expires_in: Int?
let message: String?
}
So now you have to deal with all the optionals.
Another way that seems more appropriate is to introduce another Struct that holds success and failure but you have to implement the init(from decoder: Decoder) method as below,
struct LoginFailure: Codable {
let message:String
}
struct LoginSuccess: Codable {
let user_id: Int
let token: String
let token_type : String
let expires_in: Int
}
struct LoginRespone: Codable {
var data: LoginSuccess?
var message: LoginFailure?
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let data = try? container.decode(LoginSuccess.self) {
self.data = data
} else {
self.message = try container.decode(LoginFailure.self)
}
}
}
let success = """
{
"user_id": 41,
"token": "Token",
"token_type": "bearer",
"expires_in": 12096000
}
""".data(using: .utf8)!
let failure = """
{
"message": "this is a failure message"
}
""".data(using: .utf8)!
do {
let r = try JSONDecoder().decode(LoginRespone.self, from: failure)
print(r.message?.message)
} catch {
print(error)
}
You can use one structure to handle the response. Based on status code, you can differentaite the response.
struct LoginModel:Codable {
let message:String
let userId: Int
let token: String
let tokenType : String
let expiresIn: Int
enum CodingKeys : String,CodingKey {
case message, token
case userId = "user_id"
case tokenType = "token_type"
case expiresIn = "expires_in"
}
init(from decoder: Decoder) throws {
let value = try decoder.container(keyedBy: CodingKeys.self)
self.message = try value.decodeIfPresent(String.self, forKey: .message) ?? ""
self.userId = try value.decodeIfPresent(Int.self, forKey: .userId) ?? 0
self.token = try value.decodeIfPresent(String.self, forKey: .token) ?? ""
self.tokenType = try value.decodeIfPresent(String.self, forKey: .tokenType) ?? ""
self.expiresIn = try value.decodeIfPresent(Int.self, forKey: .expiresIn) ?? 0
}
}

Decode data of Object type and generic type for base JSON response using Decodable

I have a base model -
struct BaseModel<T:Decodable>: Decodable {
let jsonData: [T]?
let status: Bool?
let message: String?
enum CodingKeys: String, CodingKey {
case jsonData = "data"
case status = "success"
case message
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
if let string = try container.decodeIfPresent(T.self, forKey: .jsonData) {
print(string)
jsonData = [string]
} else {
jsonData = nil
}
} catch DecodingError.typeMismatch {
jsonData = try container.decodeIfPresent([T].self, forKey: .jsonData)
}
status = try container.decodeIfPresent(Bool.self, forKey: .status)
message = try container.decodeIfPresent(String.self, forKey: .message)
}
}
I am getting response in two types under jsonData
Object
Array
Getting error while decoding if I receive response as Object. And if I choose let jsonData: T?, Then getting issue in decoding of Array response.
I am using this model in my Network Model. That looks like -
func performOperation<T:Decodable>(urlEndPoint: String, method: HTTPMethod, param: Parameters?, isJsonAvailable: Bool, completion: #escaping(_ response: T?, [T]?, String?, Bool?) ->Void) {
AF.request(urlEndPoint, method: method, parameters: param, headers: header).validate(statusCode: 200..<500).responseDecodable(of: BaseModel<T>.self, decoder: decoder) { (response) in
}
Json response in case of Object -
{
"success": true,
"data": {
"heading": "Same text 1",
"title": "Sample Text 2",
"content": "Sample text 3"
},
"message": "Api response received"
}
Json response in case of ArrayList -
{
"success": true,
"data": [
{
"id": 1,
"name": "Home"
},
{
"id": 2,
"name": "Profile"
}
],
"message": "Menu List"
}
You don't need a generic structure. Just create a optional property to assign your object in case there is no user array:
struct BaseModel {
let data: [User]
let post: Post?
let success: Bool
let message: String
}
struct User: Codable {
let id: Int
let name: String
}
struct Post: Codable {
let heading: String
let title: String
let content: String
}
extension BaseModel: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
data = try container.decode([User].self, forKey: .data)
post = nil
} catch DecodingError.typeMismatch {
data = []
post = try container.decode(Post.self, forKey: .data)
}
success = try container.decode(Bool.self, forKey: .success)
message = try container.decode(String.self, forKey: .message)
}
}
If there is other responses not shown in your post you can do the same approach above using a generic structure as well:
struct BaseModel<T: Codable> {
let array: [T]
let element: T?
let success: Bool
let message: String
}
extension BaseModel: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
array = try container.decode([T].self, forKey: .array)
element = nil
} catch DecodingError.typeMismatch {
array = []
element = try container.decode(T.self, forKey: .array)
}
success = try container.decode(Bool.self, forKey: .success)
message = try container.decode(String.self, forKey: .message)
}
}

Array vs Dictionary response structures with JSONDecoder

Got the following data model:
class ResponseMultipleElements<Element: Decodable>: Decodable {
let statuscode: Int
let response_type: Int
let errormessage: String?
let detailresponse: Element?
}
class Element<T: Decodable>: Decodable {
let count: String;
let element: T?
}
For the following API response structure:
{
"statuscode": 200,
"response_type": 3,
"errormessage": null,
"detailresponse": {
"count": "1",
"campaigns": [
{
"id": 1,
"name": "Foo",
"targetagegroup": null,
"creator":...
...
}
}
}
I'm triggering JSONDecoder like this:
class APIService: NSObject {
func getCampaignList(completion: #escaping(Result<[Campaign], APIError>) -> Void) {
guard let endpoint = URL(string: apiBaseUrlSecure + "/campaignlist") else {fatalError()}
var request = URLRequest(url: endpoint)
request.addValue("Bearer " + UserDefaults.standard.string(forKey: "authtoken")!, forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let jsonData = data
else { print("ERROR: ", error ?? "unknown error"); completion(.failure(.responseError)); return }
do {
let response = try JSONDecoder().decode(ResponseMultipleElements<[Campaign]>.self, from: jsonData)
completion(.success(response.detailresponse!))
} catch {
print("Error is: ", error)
completion(.failure(.decodingError))
}
}
dataTask.resume()
}
...
}
And I'm finally trying to make use of the decoded campaign object like this
class CoopOverviewViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
//do stuff
// load Campaigns
self.apiService.getCampaignList(completion: {result in
switch result {
case .success(let campaigns):
DispatchQueue.main.async {
print("CAMPAIGN DATA: ", campaigns[0].name)
}
case .failure(let error):
print("An error occured \(error.localizedDescription)")
}
})
...
}
Now I've got 2 questions:
1)
let element: T?
is actually called "campaigns" in the api response for this call. However, it could be cooperations, payments, etc. in other api responses with that same ResponseMultipleElements surrounding structure. Is there a way to make the key swappable here, like I've done with the value with the use of generics? If not, how else would I solve that problem?
2) I'm getting this error:
typeMismatch(Swift.Array<Any>,
Swift.DecodingError.Context(codingPath:
[CodingKeys(stringValue: "detailresponse", intValue: nil)],
debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
I've told Swift that the "campaigns" part of the detailresponse is an Array of campaign objects - at least that's my understanding when looking at the api response. However, the error seems to say it's a dictionary. First, I don't get why that is and would really like to understand it. Second, I don't know how to tell it that it should expect a dictionary instead of an array then - getting confused with generics here a bit.
Thank you so much for your help in advance!
This is an approach to add a custom key decoding strategy to map any CodingKey but count in detailresponse to fixed value element.
First of all create a custom CodingKey
struct AnyCodingKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) {
return nil
}
}
Then create the structs similar to Sh_Khan's answer, in most cases classes are not needed
struct ResponseMultipleElements<T: Decodable>: Decodable {
let statuscode : Int
let response_type : Int
let errormessage : String?
let detailresponse : Element<T>
}
struct Element<U: Decodable>: Decodable {
let count : String
let element : U
}
struct Campaign : Decodable {
let id : Int
let name : String
let targetagegroup : String?
}
Now comes the funny part. Create a custom key decoding strategy which returns always element for the CodingKey in detailresponse which is not count
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom { codingKeys in
let lastKey = codingKeys.last!
if lastKey.intValue != nil || codingKeys.count != 2 { return lastKey }
if lastKey.stringValue == "count" { return lastKey }
return AnyCodingKey(stringValue: "element")!
}
let result = try decoder.decode(ResponseMultipleElements<[Campaign]>.self, from: data)
completion(.success(result.detailresponse.element))
} catch {
print("Error is: ", error)
completion(.failure(error))
}

Data fetched via postman and URLSession is different in here http://173.249.20.137:9000/apiapp/coupon

http://173.249.20.137:9000/apiapp/coupon GET method .
when I request via URLSession and Postman I get two different results. Actually postman data is correct, but the URL session has always the same response whether I add or delete data it is not going to update. Can anybody please give a look. if it is happening with me only or something wrong at the backend server.
I have tested requesting data with URLSession.shared and postman.
I actually like to have the data I get via postman through URLSession request too.
func getAvailableCoupons(urlString:String, completion: #escaping (_
product: Any, _ error: Error?) -> Void){
guard let url = URL(string: urlString) else {return}
let task = URLSession.shared.dataTask(with: url) { (data, response,
error) in
let jsonDecoder = JSONDecoder()
guard let dataResponse = data,
error == nil else {
print(error?.localizedDescription ?? "Response Error")
return }
let statusCode = (response as! HTTPURLResponse).statusCode
let responseJSON = try? JSONSerialization.jsonObject(with: dataResponse, options: [])
if let responseJSON = responseJSON as? [String: Any] {
if statusCode == 200 {
do {
let jsonData = try JSONSerialization.data(withJSONObject: responseJSON, options: [])
let responseData = try jsonDecoder.decode(CoupensResponseModel.self, from:jsonData)
completion(responseData, nil)
} catch let error {
print("error when parshing json response \(error)")
completion(error, nil )
}
} else if statusCode == 404{
completion(" 404 not found", nil )
} else {
print("fatal error \(error?.localizedDescription ?? "big errror")")
}
}
}
task.resume()
}
import Foundation
// MARK: - CoupensResponseModel
struct CoupensResponseModel: Codable {
let couponDetails: [CouponDetail]?
enum CodingKeys: String, CodingKey {
case couponDetails = "coupon_details"
}
}
// MARK: - CouponDetail
struct CouponDetail: Codable {
let id: Int?
let vouchersusageSet: [VouchersusageSet]?
let couponCode: String?
let minimumSpend: Int?
let expiryDate, createdDate: String?
let discountPrice, discountPercent: Int?
let discountBasis: DiscountBasis?
let couponImage: String?
let couponType: String?
enum CodingKeys: String, CodingKey {
case id
case vouchersusageSet = "vouchersusage_set"
case couponCode = "coupon_code"
case minimumSpend = "minimum_spend"
case expiryDate = "expiry_date"
case createdDate = "created_date"
case discountPrice = "discount_price"
case discountPercent = "discount_percent"
case discountBasis = "discount_basis"
case couponImage = "coupon_image"
case couponType = "coupon_type"
}
}
enum DiscountBasis: String, Codable {
case amount = "amount"
case percent = "percent"
}
// MARK: - VouchersusageSet
struct VouchersusageSet: Codable {
let id: Int?
let itemObj: ItemObj?
let voucherObj: Int?
let sectionObj, categoryObj: Int?
}
// MARK: - ItemObj
struct ItemObj: Codable {
let id: Int?
let code, productName: String?
let productPrice, discountedPrice: Int?
let productDescription, itemImage: String?
let categoryObj: CouponCategoryObj?
enum CodingKeys: String, CodingKey {
case id, code
case productName = "product_name"
case productPrice = "product_price"
case discountedPrice = "discounted_price"
case productDescription = "product_description"
case itemImage = "item_image"
case categoryObj
}
}
// MARK: - CouponCategoryObj
struct CouponCategoryObj: Codable {
let id: Int?
let categoryCode, categoryName: String?
let categoryImage: CouponJSONNull?
let sectionObj: Int?
enum CodingKeys: String, CodingKey {
case id
case categoryCode = "category_code"
case categoryName = "category_name"
case categoryImage = "category_image"
case sectionObj
}
}
// MARK: - Encode/decode helpers
class CouponJSONNull: Codable, Hashable {
public static func == (lhs: CouponJSONNull, rhs: CouponJSONNull) ->
Bool {
return true
}
public var hashValue: Int {
return 0
}
public func hash(into hasher: inout Hasher) {
// No-op
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(CouponJSONNull.self,
DecodingError.Context(codingPath: decoder.codingPath,
debugDescription:
"Wrong type for CouponJSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
Try the following method
let headers = [
"Cache-Control": "no-cache",
]
let request = NSMutableURLRequest(url: NSURL(string: "http://173.249.20.137:9000/apiapp/coupon")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let string = String(data: data!, encoding: .utf8) ?? ""
print(string)
}
})
dataTask.resume()
Please look on response both are same.
Postman response :
https://jsoneditoronline.org/?id=7b94ef69a3344164aa4a96423fdbf9db
Code response :
https://jsoneditoronline.org/?id=6e5a7d221d9c4c818f1d46fc893031fe