Facing Problem in encoding array of dictionary to JSON - json

In POST request I have to send following JSON data
{
"users": [{"userid": 16, "meetAt":"Some place (College/trip etc)", "showFields": "11111111000"}, {"userid": 17, "meetAt":"Some place (College/trip etc)", "showFields": "11111111001"}]
}
I am trying
static func linkRequestBody(userDetails: ScannedContact) -> Any
{
let jsonToRegistrer = [["userid":userDetails.id, "meetAt":"Defalut Test Location at", "showFields":userDetails.showFields]];
return jsonToRegistrer;
}
I can see in debugger that userDetails.id and userDetails.showFields have valid value but still it fails.
ERROR:
{"":["The input was not valid."]}

That's your target format:
{
"users": [{
"userid": 16,
"meetAt": "Some place (College/trip etc)",
"showFields": "11111111000"
}, {
"userid": 17,
"meetAt": "Some place (College/trip etc)",
"showFields": "11111111001"
}]
}
After calling JSONSerialization on it (or if your code accept a Dictionary/Array and do the translation itself):
let jsonToRegistrer = [["userid":userDetails.id, "meetAt":"Defalut Test Location at", "showFields":userDetails.showFields]];
Should represent that:
[{
"userid": 16,
"meetAt": "Defalut Test Location at",
"showFields": "11111111000"
}]
You see the issue? Your version is an Array and the target one is a Dictionary and you are so missing the users key.
To fix it:
let jsonToRegistrer = ["user": [["userid":userDetails.id, "meetAt":"Defalut Test Location at", "showFields":userDetails.showFields]]];
You can also not write it in one line to be clearer:
let jsonToRegistrer = ["user": [
["userid": userDetails.id,
"meetAt": "Defalut Test Location at",
"showFields": userDetails.showFields]
]
];
So the issue is that your version didn't have the same format at the one needed.
To see what's your version rendered in JSON you can use:
let jsonData = try? JSONSerialization.data(withJSONObject: jsonToRegistrer, options: .prettyPrinted)
let jsonString = String(data: jsonData!, encoding: .utf8) //Note it's a force unwrap but just to debug, avoid using force unwrap
print("jsonString: \(jsonString)")

I would personally avoid the problem by using struct thant implements Codable. As you don't have any value of type Any nor recursive field, this should work very easily
struct Users: Codable {
var users: [User]
}
struct User: Codable {
let userid: Int
let meetAt: String
let showFields: String
}
Then you just have to bind User in Users.users can encode it easily doing
do {
let json = try? JSONEncoder().encode(Users)
} catch { // whatever you want }
This article might help you to understand better Codable protocol.
Hope it helps !

Related

I expect the result to be a dictionary but it is accessed like a property... why?

I have this JSON coming from a server...
{
"cars": [
{
"name": "Ferrari",
"price": "100"
},
{
"name": "Lamborghini",
"price": "200"
},
{
"name": "Ford Pinto",
"price": "1"
}
]
}
This JSON is a dictionary called cars that contains an array of cars, right?
Then I have this struct...
struct Cars: Codable {
let cars: [Car]
}
struct Car: Codable, Hashable, Identifiable {
let id = UUID()
let name: String
let price: String
}
and I decode the JSON using this:
let (data, _) = try await urlSession.data(from: url)
let result = try JSONDecoder().decode(Cars.self, from: data)
let listOfCars = result.cars
This is something I don't understand.
in result.cars, cars is a property of result that was declared as an array in the struct Cars. Not a dictionary.
I was expecting to access it using result["cars"].
Why is that?
In your code here...
let (data, _) = try await urlSession.data(from: url)
let result = try JSONDecoder().decode(Cars.self, from: data)
let listOfCars = result.cars
result is an instance of the Struct Cars. Your set up code has told Swift how to translate from a JSON dictionary into your own Struct.
So everything inside of result is accessed just like how you would access it in the following...
let result = Cars(cars: [
Car(name: "Ford", price: "£10,000")
])
print(result.cars)
The only difference is how you are creating it. Instead of using the init method like this you are using a JSON decode to decode some JSON into your custom type.
As said in the comments and answers, it takes a result type according to your decode strategy. In your code result type is Cars not a dictionary. So you access the properties with using result.cars
If you want something as dictionary instead, you need to decode it like
let result = try decode.decode([String : [Car]].self, from: data)
Now you can access them like a dictionar
print(result["cars"]?.first?.name) // Optional("Ferrari")

How to decode this JSON data in Swift

I am trying to get the title from json data but I am getting an error. Here is my model object
struct SpaceNewsModel: Identifiable, Codable {
var id = UUID()
let title: String
let url: String
let source: String
}
And when I get to this point in my networking, I get an error.
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
if error != nil {
print(error!)
} else {
let decoder = JSONDecoder()
if let safeData = data {
do {
let astroNews = try decoder.decode([SpaceNewsModel].self, from: safeData)
print(astroNews[0].title)
} catch {
print("DEBUG: Error getting news articles \(error.localizedDescription)")
}
}
let httpResponse = response as? HTTPURLResponse
// print(httpResponse)
}
})
task.resume()
}
My main problem is with the line print(astroNews[0].title I am not formatting it right and I don't know how to access this data whether in an array or otherwise.
Here is the returned JSON data after making the request on the website postman.
[
{
"title": "Wales' new £2bn space strategy hopes",
"url": "https://www.bbc.co.uk/news/uk-wales-60433763",
"source": "bbc"
},
{
"title": "Could Port Talbot become a centre of space tech? Video, 00:02:02Could Port Talbot become a centre of space tech?",
"url": "https://www.bbc.co.uk/news/uk-wales-60471170",
"source": "bbc"
}
]
Normally there would be a title for each that I can access but here it is just the data. So to summarize, how would I get the first or second title in this JSON data? Because astroNews[0].title does not work.
EDIT: I want to clarify that if I were to decode an empty struct, I would not get any errors.
struct EMPTYSTRUCT: Codable {
}
But as soon as I add any variable like let title: String I get an error. I believe something is wrong in the formatting of the JSON because usually the JSON would look something this
{
"coord": {
"lon": -95.3633,
"lat": 29.7633
}
}
where I could name the struct "coord" however no such names exist in the returned JSON I'm working with. Only the variable names. The confusion is how would I construct my SpaceNewModel file to work with the returned JSON with no apparent object name.
With reference to this answer.
Just change your structure to
struct SpaceNewsModel: Identifiable, Codable {
let id = UUID()
let title: String
let url: String
let source: String
}
This is not the solution but a workaround; if you want to solve the issue, refer to this answer

How to access varying JSON key in Swift 4.2 natively?

I have the following dummy JSON data for a bus to school.
{
"toSchool": {
"weekday": [{
"hour": 7,
"min": 10,
"type": null,
"rotary": false
}],
"sat": [{
"hour": 8,
"min": 15,
"type": null,
"rotary": true
}]
}
}
I would like to access "weekday" and "sat" key with a variable based on user input. How can I achieve this natively?
Using SwiftyJSON, it is fairly simple like below
let json = try JSON(data: data)
let userDirection = "shosfc"
let userWeek = "weekday"
let busList = json[userDirection][0][userWeek]
However, I was wondering how this would be done natively to remove dependencies.
It seems that CodingKey and enum might be the way to handle this. When the example is as simple as this, I can understand. However, I just cannot get my head around it for my particular usage where it involves custom objects not just String.
How can I do this? Please help.
This is based on your earlier question
func bus(isWeekday: Bool = true) -> [Bus] {
return isWeekday ? shosfc.weekDay : shosfc.sat
}
I think code that below will work:
struct SampleResponse: Codable {
let toSchool: ToSchool
}
struct ToSchool: Codable {
let weekday, sat: [Sat]
}
struct Sat: Codable {
let hour, min: Int
let type: String?
let rotary: Bool
}
To decode this type of response, you must decode this JSON with SampleResponse type.
let sampleResponse = try? newJSONDecoder().decode(SampleResponse.self, from: jsonData)
After that, you can reach variables like you asked.
You can convert JSON string to Dictionary in swift and access it the same way you just did:
func parseToDictionary(_ jsonStr: String) -> [String: Any]? {
if let data = jsonStr.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
let jsonStr = "{Your JSON String}"
let json = parseToDictionary(jsonStr)
let userDirection = "shosfc"
let userWeek = "weekday"
let busList = json[userDirection][0][userWeek]

Freddy JSON - copying the collection into a new array

In Freddy JSON with Swift, an extension exists to...
"Create an instance by copying each element of the collection into a new Array", as defined in the Freddy notes below...
public init<Collection>(_ collection: Collection) where Collection : Collection, Collection.Element == Freddy.JSON
// Create an instance initialized with `elements`.
public init(arrayLiteral elements: Freddy.JSON...)
Given that I already have the data, how is this initializer used? Transforming the data in the usual way as below is fine, but I am not sure on the syntax to copy the data as a collection.
let json = try JSON(data: data)
If it helps, my intention is to get the content of each array and create a new object from it.
[{
"array1": [{
"array1keys": "example",
}],
"array2": [{
"array2keys": "example"
}]
}]
Why don't you use the Codable protocol provided out of the box by Swift?
Given your json
let data = """
[
{
"array1": [
{
"array1keys": "example",
}
],
"array2": [
{
"array2keys": "example"
}
]
}
]
""".data(using: .utf8)!
We can define a type representing your data
typealias SubElement = [String:String]
typealias Element = [String:[SubElement]]
typealias Response = [Element]
And finally we can decode your json
if let response = try? JSONDecoder().decode(Response.self, from: data) {
print(response)
}
[["array1": [["array1keys": "example"]], "array2": [["array2keys": "example"]]]]
Usually I would suggest to define a struct conform to Codable which makes the code much more easy to read. But in this case this is not possible since there are no fields with a fixed name.

parsing JSON with a decodable?

I have a JSON file:
{
"name": "Jens",
"time": "11.45",
"date": "2018:04:17",
"differentTimestamps":[""]
"aWholeLotOfnames":{
"name1": "Karl"
"name2": "pär"
}
How to parse above JSON ? I have checked this tutorial https://www.youtube.com/watch?v=YY3bTxgxWss. One text tutorial to but i don't get how to make a variable that can take a
"nameOfVar"{}
If it's not a dictionary. The tutorial are using a var nameOfVar: [what should be here in this case] for one that nearly looks like it. The thing is though that theirs are starting with a [{ and ends with a }] while mine only starts with a {? i don't know how to solve this?
Creating corresponding Swift data types for JSON is very easy.
A dictionary {} can be decoded into a class / struct where the keys become properties / members.
An array [] can be decoded into an array of the given (decodable) type.
Any value in double quotes is String even "12" or "false".
Numeric floating point values are Double, integer values are Int and true / false is Bool
null is nil
let jsonString = """
{
"name": "Jens",
"time": "11.45",
"date": "2018:04:17",
"differentTimestamps":[""],
"aWholeLotOfnames":{
"name1": "Karl",
"name2": "pär"
}
}
"""
struct Item: Decodable {
let name, time, date: String
let differentTimestamps: [String]
let aWholeLotOfnames: AWholeLotOfnames
}
struct AWholeLotOfnames : Decodable {
let name1, name2 : String
}
let data = Data(jsonString.utf8)
do {
let result = try JSONDecoder().decode(Item.self, from: data)
print(result)
} catch { print(error) }