TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Still having problems understanding how to create a Struct to extract from a JSON

Forums > Swift

Hi everyone, I posted a question yesterday concerning my challenges creating a Struct to get data out of a JSON. I addressed the Any? issue thank to the help from @ygeras. But i'm still stuck.

I want simply to select the following data frol the JSON:

  var name: String
  var relevance: String
  var type: String
  var label: String

My Struct looks like this

struct ABB: Codable {
    var results: ResultAB
}

struct ResultAB: Codable {
    var nameAB: String
    var relevanceAB: String?
    var typeAB: String
    var labelAB: String

    enum CodingKeys: String, CodingKey {
        case nameAB
        case relevanceAB
        case typeAB
        case labelAB
    }
}

And I decode like this

    let ABQuery = try JSONDecoder().decode([ABB].self, from: caData)

But I get the error:

 Type mismatch for type Array<Any> in JSON: Expected to decode Array<Any> but found a dictionary instead.

Think is the JSON is complex (at least for me) and so i'm not understanding how to construct a working Struct. Rather than past the JSON here, here is a link because is quite long.

https://1drv.ms/u/s!Ahk-sk_KQft1jIVx2MBugOCmZy74lA

If anyone can gve me any pointers , that would be amazing.

Thank you!

3      

Hi, Could you post the URL?

3      

Not sure if those are exactly the items you are trying to extract. But to me the hierarchy seems as follows:

// Results contain actions as an array
struct Result: Codable {
    var actions: [Action]
}

// Actions contain array of items with criteria, type, label, etc...
struct Action: Codable {
    var criteria: [Criteria]
    var type: String
    var label: String
}

// Criteria contains variable name, relevance etc....
struct Criteria: Codable {
    var name: String
    var relevance: String
}

3      

There is alot of field.

The struct ABB is a dictionary not an array

let ABQuery = try JSONDecoder().decode(ABB.self, from: caData) // <- remove the [ ]
// This is a dictionary which contains the other data 
// This is what it look at first
struct ABB: Codable {
  let results: Result 
}

// This contains an array of Action
struct Result: Codable {
  let actions: [Action]
}

// This contains an array of Criterium and dictionary fields
struct Action: Codable {
  let criteria: [Criterium]
  let type: String
  let label: String
}

// This contains dictionary fields
struct Criterium: Codable {
  let name: String
  let relevance: String
}

Find the field(s) that you want eg

"We detected some indexable pages with less than 4 inlinks. Employing a link-building strategy is an important part of your site's SEO, especially for pages for which it is important to attract users from organic search. Ensure that inlinks to priority indexable pages grow to at least 5 (recommended level) which helps to improve their PageRank"

Then trace the "text" -> "description" -> "actions" -> "results" -> Main So to get to that

struct ABB: Codable {
  let results: Result 
}

struct Result: Codable {
  let actions: [Action]
}

struct Action: Codable {
 let description: Description
}

struct Description: Codable {
  let text: String
}
let text = ABB(results:
                    Result(actions: [
                        Action(description:
                                Description(text: "We detected some indexable pages with less than 4 inlinks. Employing a link-building strategy is an important part of your site's SEO, especially for pages for which it is important to attract users from organic search. Ensure that inlinks to priority indexable pages grow to at least 5 (recommended level) which helps to improve their PageRank"))
                    ]))

3      

@Hectorcrdna, apologies I can't because i'd have to give you a token too and i'm not allowed to share that. @NigelGee, thank you for that. I feeling dumb because i'm not sure how to implement your suggestion. I understand what a Dictionary is generally speaking but in this example with a dictionary/struct i'm lost at the moment.

I changed the struct to

// This is a dictionary which contains the other data 
// This is what it look at first
struct ABB: Codable {
  let results: Result 
}

// This contains an array of Action
struct Result: Codable {
  let actions: [Action]
}

// This contains an array of Criterium and dictionary fields
struct Action: Codable {
  let criteria: [Criterium]
  let type: String
  let label: String
}

// This contains dictionary fields
struct Criterium: Codable {
  let name: String
  let relevance: String
}

and then used

let ABQuery = try JSONDecoder().decode(ABB.self, from: caData) // <- remove the [ ]

and I get

Type mismatch for type Array<Any> in JSON: Expected to decode Array<Any> but found a dictionary instead.

I guess I don't understand how implement your suggestions. Apologies for my lack of knowledge on this one. I'm gonna have to search youtube to learn more about this Struct thing.

3      

Without the URL to test but what i have do is copied the JSON you into the bundle and tried from there

struct ContentView: View {
    let abb = Bundle.main.decode(ABB.self, from: "JSONExample.json")
    var body: some View {
        VStack {
            List(abb.results.actions, id: \.self) { data in
                Text(data.label)
            }
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

extension Bundle {
    func decode<T: Decodable>(
        _ type: T.Type,
        from file: String,
        dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate,
        keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys
    ) -> T {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle")
        }

        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle")
        }

        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = dateDecodingStrategy
        decoder.keyDecodingStrategy = keyDecodingStrategy

        // swiftlint:disable line_length
        do {
            return try decoder.decode(T.self, from: data)
        } catch DecodingError.keyNotFound(let key, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' - \(context.debugDescription)")
        } catch DecodingError.typeMismatch(_, let context) {
            fatalError("Failed to decode \(file) from bundle due to type mismatch - \(context.debugDescription)")
        } catch DecodingError.valueNotFound(let type, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing \(type) value = \(context.debugDescription)")
        } catch DecodingError.dataCorrupted(_) {
            fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON.")
        } catch {
            fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)")
        }
    }
}

struct ABB: Codable {
  let results: Result
}

// This contains an array of Action
struct Result: Codable, Hashable {
  let actions: [Action]
}

// This contains an array of Criterium and dictionary fields
struct Action: Codable, Hashable {
  let criteria: [Criterium]
  let type: String
  let label: String
}

// This contains dictionary fields
struct Criterium: Codable, Hashable {
  let name: String
  let relevance: String
}

I have Data

ScreenShot

So it will be with the call for the data not the structs

3      

@NigelGee Thank you! Thank you! Putting it all in context is much clearer! So happy. I'll spend some quality time this weekend learning more about Structs and APIs :)

And BTW I also learned about Bundles, so thanks for that. It's really useful because I'll be spending 36 hours on aircraft over the next 2 weeks without wifi so i'll be able to experiment and test offline :)

3      

Curly braces {} hold objects. The data are in key, value pairs. Square brackets [] hold arrays. Each data element is enclosed with quotes if it's a character, or without quotes if it is a numeric value. Commas are used to separate pieces of data.

3      

Hacking with Swift is sponsored by Superwall.

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

Learn More

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.