0

I've noticed that while Quicktype.io does a very good job of parsing JSON into SWIFt, occasionally it resorts to a lot of auxiliary functions and methods. For the following, it generated about 15 additional methods. Some of these are familiar such as NSNull, however, two are unfamiliar to me such as JSONAny and it seems like there ought to be a way around them. The JSONAny class for example, has about 12 functions in it and it is used to parse just one field that is not that important to me.

Here is what the JSON looks like:

[{"name":"Afghanistan","topLevelDomain":[".af"],"alpha2Code":"AF","alpha3Code":"AFG","callingCodes":["93"],"capital":"Kabul","altSpellings":["AF","Afġānistān"],"region":"Asia","subregion":"Southern Asia","population":27657145,"latlng":[33.0,65.0],"demonym":"Afghan","area":652230.0,"gini":27.8,"timezones":["UTC+04:30"],"borders":["IRN","PAK","TKM","UZB","TJK","CHN"],"nativeName":"افغانستان","numericCode":"004","currencies":[{"code":"AFN","name":"Afghan afghani","symbol":"؋"}],"languages":[{"iso639_1":"ps","iso639_2":"pus","name":"Pashto","nativeName":"پښتو"},{"iso639_1":"uz","iso639_2":"uzb","name":"Uzbek","nativeName":"Oʻzbek"},{"iso639_1":"tk","iso639_2":"tuk","name":"Turkmen","nativeName":"Türkmen"}],"translations":{"de":"Afghanistan","es":"Afganistán","fr":"Afghanistan","ja":"アフガニスタン","it":"Afghanistan","br":"Afeganistão","pt":"Afeganistão","nl":"Afghanistan","hr":"Afganistan","fa":"افغانستان"},"flag":"https://restcountries.eu/data/afg.svg","regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation","otherAcronyms":[],"otherNames":[]}],"cioc":"AFG"}]

I wont' give all the code that that struct is one level down from the main struct:

 struct CountryReturnedElement: Codable {
       //...various fields  
        let regionalBlocs: [RegionalBloc]
    }

   // MARK: - RegionalBloc
    struct RegionalBloc: Codable {
        let acronym, name: String
        let otherAcronyms, otherNames: [JSONAny]
    }

Which is designed to decode merely the following JSON:

"regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation","otherAcronyms":[],"otherNames":[]}]

Is there a simple way to parse the above without resorting to auxiliary classes with literally fifteen functions and methods. I all likelihood, otherAcronyms and otherNames are strings so I guess I could go [String?]. However, I guess I don't know that with 100% certainty, more like 95% certainty.

Thanks for any suggestions.

user6631314
  • 1,751
  • 1
  • 13
  • 44
  • 2
    What is the content of the otherAcronyms and otherNames arrays? – Joakim Danielson Jul 28 '20 at 16:21
  • In the examples, it is empty which I guess is why it created the Any. Even if it has to use Any, I'm surprised that Any is so complicated. I did some more spot checking and it does look like it's usually a String, though it can be a string in another character set as in: "جامعة الدول العربية". Do you think I'm okay with [String?] – user6631314 Jul 28 '20 at 16:25
  • `[JSONAny]` is provided in case the incoming format of the value is unknown. – Frankenstein Jul 28 '20 at 16:32
  • 1
    Yes you should be OK with [String] for the array then. No need for an optional string unless you expect actual null/nil values. – Joakim Danielson Jul 28 '20 at 17:20
  • Tip for anyone trying to debug a similar issue who comes across this question. Take the JSON with missing values, copy it to JSONLint and then fill in the missing values, nulls etc with what you would get if the API was fully populated and fully formed. Copy and paste this text into QuickType and it will give you improved suggestions for your structs. Then you can fine tune. I ended up using optionals for missing values. There may be a better way but this worked for me. – user6631314 Jul 29 '20 at 22:13

1 Answers1

1

If you're certain the otherAcronyms and otherNames keys return [String?] you can modify the RegionalBloc struct to accept [String?].

struct RegionalBloc: Codable {
    let acronym, name: String
    let otherAcronyms, otherNames: [String?]
}

You can simply try it out and if the JSONDecoder doesn't throw any error you're good and can continue with [String?]. Otherwise, you can check the error and print it out onto the console to check the incoming type and set it.

Frankenstein
  • 15,732
  • 4
  • 22
  • 47
  • With both [String] and [String?], after writing some code for testing, for following JSON I'm getting following error: "regionalBlocs":[{"acronym":"EU","name":"European Union","otherAcronyms":[],"otherNames":[]}],"cioc":"FRA"}]} typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil)) – user6631314 Jul 29 '20 at 11:21
  • As I've mentioned in the answer, the error clearly states that the incoming type is a dictionary. So, you have to provide another codable `struct` model instead of `[String?]` – Frankenstein Jul 29 '20 at 11:26
  • By dictionary, I guess it means: "otherNames":[]. I know that the dictionary value if not empty will be an array of one or more strings such as["Comunidad del Caribe","Communauté Caribéenne"] but I guess it doesn't know that. I saw some answers on SO that suggest you need to use your own init(from to handle empty arrays. https://stackoverflow.com/questions/48943510/swift-codable-decode-empty-json-as-nil-or-empty-object – user6631314 Jul 29 '20 at 14:31
  • No, dictionary means [:]. But, in your question, you've depicted `otherNames` as an array, which is []. – Frankenstein Jul 29 '20 at 15:19
  • It is [], not [:] The actual JSON is "regionalBlocs":[{"acronym":"EU","name":"European Union","otherAcronyms":[],"otherNames":[]}]. It seems to be failing on "otherAcronyms":[] I was able to successfully decode: {"test":[]} using let test : [String?] but stumped on full example. Does it matter that regionalBlocs seems to be an array of items eg [{}]? – user6631314 Jul 29 '20 at 15:47
  • Yes it does matter if you're not providing the right type your decoding will fail. If you've the full example then use that to create your model in quicktype. If you're sure it's [] then it's an array. But, the error message states otherwise. – Frankenstein Jul 29 '20 at 16:01
  • I added a new level to reflect the array so I now am using struct RegionalBlocs: Codable { let regionalBloc: [RegionalBloc] } struct RegionalBloc: Codable { let acronym, name: String let otherAcronyms, otherNames: [String] } However, it still yields the same error. There is absolutely no dictionary in the []. I tested it by just copying the json into a string as opposed to a live feed....Mystifying. Maybe I will delete all these things from the string. – user6631314 Jul 29 '20 at 17:12
  • Finally got it to work by filling in all missing values in JSON with dummy values, getting improved suggestions from quicktype and fine tuning a bit. – user6631314 Jul 29 '20 at 22:15