How to Create a Struct to Match This JSON

How to create a struct to match this Json

You want to learn how to do this on your own but there are tools on the web that can easily help you create a struct from a JSON. This is a really good one.

You were close so make sure you dissect the code and notice the differences

import SwiftUI
class ResultsViewModel: ObservableObject{
let json = """
{
"results": [
{
"gender": "male",
"name": {
"title": "mr",
"first": "brad",
"last": "gibson"
},
"location": {
"street": "9278 new road",
"city": "kilcoole",
"state": "waterford",
"postcode": "93027",
"coordinates": {
"latitude": "20.9267",
"longitude": "-7.9310"
},
"timezone": {
"offset": "-3:30",
"description": "Newfoundland"
}
},
"email": "brad.gibson@example.com",
"login": {
"uuid": "155e77ee-ba6d-486f-95ce-0e0c0fb4b919",
"username": "silverswan131",
"password": "firewall",
"salt": "TQA1Gz7x",
"md5": "dc523cb313b63dfe5be2140b0c05b3bc",
"sha1": "7a4aa07d1bedcc6bcf4b7f8856643492c191540d",
"sha256": "74364e96174afa7d17ee52dd2c9c7a4651fe1254f471a78bda0190135dcd3480"
},
"dob": {
"date": "1993-07-20T09:44:18.674Z",
"age": 26
},
"registered": {
"date": "2002-05-21T10:59:49.966Z",
"age": 17
},
"phone": "011-962-7516",
"cell": "081-454-0666",
"id": {
"name": "PPS",
"value": "0390511T"
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/75.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/75.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/75.jpg"
},
"nat": "IE"
}
],
"info": {
"seed": "fea8be3e64777240",
"results": 1,
"page": 1,
"version": "1.3"
}
}
""".data(using: .utf8)

var result: JSONObject? {
var decoded: JSONObject? = nil
do{
decoded = try JSONDecoder().decode(JSONObject.self, from: json!)
}catch{
print(error)
}
return decoded
}
}

struct ResultsView: View {
@StateObject var vm: ResultsViewModel = ResultsViewModel()
var body: some View {
if vm.result == nil || vm.result?.results.first == nil{
Text("nil")
}else{
VStack{
Text(vm.result!.results.first!.name.first)
Text(vm.result!.results.first!.name.last)
}
}
}
}
// MARK: - JSONObject
struct JSONObject: Codable {
let results: [Result]
let info: Info
}

// MARK: - Info
struct Info: Codable {
let seed: String
let results, page: Int
let version: String
}

// MARK: - Result
struct Result: Codable {
let gender: String
let name: Name
let location: Location
let email: String
let login: Login
let dob, registered: Dob
let phone, cell: String
let id: ID
let picture: Picture
let nat: String
}

// MARK: - Dob
struct Dob: Codable {
let date: String
let age: Int
}

// MARK: - ID
struct ID: Codable {
let name, value: String
}

// MARK: - Location
struct Location: Codable {
let street, city, state, postcode: String
let coordinates: Coordinates
let timezone: Timezone
}

// MARK: - Coordinates
struct Coordinates: Codable {
let latitude, longitude: String
}

// MARK: - Timezone
struct Timezone: Codable {
let offset, timezoneDescription: String

enum CodingKeys: String, CodingKey {
case offset
case timezoneDescription = "description"
}
}

// MARK: - Login
struct Login: Codable {
let uuid, username, password, salt: String
let md5, sha1, sha256: String
}

// MARK: - Name
struct Name: Codable {
let title, first, last: String
}

// MARK: - Picture
struct Picture: Codable {
let large, medium, thumbnail: String
}

How do I create a decodable struct to match JSON nest from API?

There are a couple of issues. Firstly, your JSON data is an array of objects and your struct is just an object. So, first create a struct to model each object. Also, using var in structs is not very useful considering structs have to be destroyed and recreated with a changed property to be modified (energy intensive process). It should look like this:


struct SingleData: Codable {
let open: Double
let high: Double
}

Now, ScrapeStruct should have a property called data that should be an array of SingleData. It should look like this:

struct ScrapeStruct: Decodable {
let data: [SingleData]
}

Now, as for your second struct. It will not work because "high" and "low" are types that do not exist. To parse (essentially converting languages, in this case JSON to Swift) you can only use basic types or objects created with basic types (such as Date, Int, String, Double, etc)

struct ScrapeStruct: Decodable {
var data: high //type high does not exist
var data: low //type low does not exist
}

How to structure my models to match my JSON?

Your APIResponse should support generic.

public class APIResponse<T>
{
public int Status { get; set; }
public string Message { get; set; }

public T Data { get; set; }
}

As the data is a LeaveModel object but not a LeaveModel array.

And deserialize as APIResponse<LeaveModel> type.

APIResponse<LeaveModel> apiResponse = JsonConvert.DeserializeObject<APIResponse<LeaveModel>>(response.Content.ReadAsStringAsync().Result);
LeaveModel leave = apiResponse.Data;

And would suggest changing the method to asynchronous.

public async Task<IActionResult> APICallAsync() 
{
//Fetch the JSON string from URL.
LeaveModel leave = new LeaveModel();
string apiUrl = "http://xxxxx.xxx.xxx/GetLeaveBalance/2170";

HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(apiUrl);
if (response.IsSuccessStatusCode)
{
APIResponse<LeaveModel> apiResponse = JsonConvert.DeserializeObject<APIResponse<LeaveModel>>(await response.Content.ReadAsStringAsync());
leave = apiResponse.Data;
}

//Return the Deserialized JSON object.
return Json(leave);
}

Creating Structs That Match JSON Content in Swift

friends is not a String array, it should also be a Struct:

struct Result: Codable {
let data: [PeopleData]
}
struct FriendData: Codable {
let name: String
let address: String
}
struct PeopleData: Codable {
let name: String
let address: String
let friends: [FriendData]
}

should work.

Nitpick: If you do not need to encode those back, Decodable should be enough instead of Codable.

P.S: QuickType is also a great tool for this.

How to make struct with this json?

I think the structs you have are very close to what is required, just minor tweaks such as:

struct Response: Codable{
var err_code: Int
var data: DataObj
}

struct DataObj: Codable{
var message: String
var result: Result
}

struct Result: Codable {
var check_in: [Checkin]
}

struct Checkin: Codable, Hashable {
var created_at: String
var late: String
var location: String
var place: String
var folder: String
var tujuan: String
var link: String
}

Use it like this:

struct ContentView: View {
@State var apiResponse: Response?

var body: some View {
VStack {
if let response = apiResponse {
Text("message: \(response.data.message) ")
Text("check_in.count: \(response.data.result.check_in.count) ")
List {
ForEach(response.data.result.check_in, id: \.self) { chk in
Text("created_at: \(chk.created_at) ")
}
}
}
}
.onAppear {
let jsonStr = """
{
"err_code": 0,
"data": {
"message": "Success",
"result": {
"check_in": [
{
"created_at": "2022-06-20 10:00:37",
"late": "True",
"location": "Jl. H. Asmawi No.63, Beji, Kecamatan Beji, Kota Depok, Jawa Barat 16421, Indonesia",
"place": "GRIT OFFICE",
"folder": "./pegawai/grit/2022/06/20/masuk/10/00/",
"filename": "0000_rakha_fatih_athallah_picture.jpg",
"tujuan": "",
"link": "http://103.140.90.10:8081/face-recognition/client/python/pegawai/grit/2022/06/20/masuk/10/00/0000_rakha_fatih_athallah_picture.jpg"
},
{
"created_at": "2022-06-16 11:23:12",
"late": "True",
"location": "Jl. H. Asmawi No.63, Beji, Kecamatan Beji, Kota Depok, Jawa Barat 16421, Indonesia",
"place": "GRIT OFFICE",
"folder": "./pegawai/grit/2022/06/16/masuk/11/23/",
"filename": "0000_rakha_fatih_athallah_picture.jpg",
"tujuan": "",
"link": "http://103.140.90.10:8081/face-recognition/client/python/pegawai/grit/2022/06/16/masuk/11/23/0000_rakha_fatih_athallah_picture.jpg"
}
],
"check_out": []
}
}
}
"""
// simulated response data
let data = jsonStr.data(using: .utf8)!
do {
self.apiResponse = try JSONDecoder().decode(Response.self, from: data)
print("\n---> apiResponse \(apiResponse)")
}
catch {
print(" error \(error)")
}
}
}
}

Swift: How to write JSON as a struct?

You could do something like this.

Firstly I converted the JSON into Data so that it is possible to decode it.

Second as there are two properties in the JSON, action and trigger, I create two nested structs that match the properties. In the second struct Trigger we need to use a custom coding key as Swift uses camelcase by convention, and you cannot use a - in a variable name.

import Foundation

let data = """
[
{
"action": {
"type": "block"
},
"trigger": {
"url-filter": "apple.com"
}
}
]
""".data(using: .utf8)!

struct SomeName: Codable {

struct Action: Codable {
let type: String
}

struct Trigger: Codable {
let urlFilter: String

enum CodingKeys: String, CodingKey {
case urlFilter = "url-filter"
}
}

let action: Action
let trigger: Trigger
}

do {
let result = try JSONDecoder().decode([SomeName].self, from: data)
} catch {
print(error)
}


Putting the above code into a Playground, it should work.

Do I need to define the exact same structure of a JSON to use it with json.Unmarshal?

You can use a struct with fields that you only want. Look at the example below

package main

import (
"encoding/json"
"fmt"
)

type Test struct {
Field1 int `json:"field1"`
Field2 string `json:"field2"`
}

func main() {

jsonString := `{
"field1": 1,
"field2": "test field 2",
"field3": "test field 3"
}`

t := Test{}

err := json.Unmarshal([]byte(jsonString), &t)

if err != nil {
fmt.Println(err)
}

fmt.Println("\n", t)
}

https://play.golang.org/p/qFLXBiU-fMX

Do you need to create a struct with every JSON property or can you be selective? Swift

You can selectively decode only the elements of the JSON object you care about. The only requirement is that your types match the JSON objects types.

So you can safely drop the values, like you have in your example.

Swift 5 : Create JSON with Codable / Struct from my variables

As you have full controll over your structure and there is no collection involved i would recommend to put everything in one struct instead of scattering it over many different:

struct UserProfile: Codable{
var id: String
var name: String
var lastname: String
var street: String
var country: String
}

Regarding caching. As you can mark this struct Codable it can be easily stored in Userdefaults as Data. This extension on Userdefaults should allow you to access UserProfile in a typesafe manner.

extension UserDefaults{
var userProfile: UserProfile?{
get{
guard let data = data(forKey: "userProfile") else{
return nil
}

return try? JSONDecoder().decode(UserProfile.self, from: data)
}
set{
set(try? JSONEncoder().encode(newValue), forKey: "userProfile")
}
}
}

Usage example:

//Write
UserDefaults.standard.userProfile = UserProfile(id: UUID().uuidString, name: "First", lastname: "Last", street: "nowhere", country: "atlantis")
//Read
let profile = UserDefaults.standard.userProfile

Edit:
Editing example:

As this is a struct it gets copied every time something changes.
So read the value in a var. Modify it and save it to the defaultStore.

var profile = UserDefaults.standard.userProfile
profile?.country = "newCountry"
UserDefaults.standard.userProfile = profile


Related Topics



Leave a reply



Submit