Freddy JSON - copying the collection into a new array - json

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.

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")

Swift Node Tree to complex Json

I'm working on a tool written with swift that needs to export a tree to json. I've been following Swift tree approach described here as a guide.
I create a tree using the following code
class Node {
var value: String
var children: [Node] = []
weak var parent: Node?
init(value: String) {
self.value = value
}
func add(child: Node) {
children.append(child)
child.parent = self
}
}
let beverages = Node(value: "beverages")
let hotBeverages = Node(value: "hot")
let coffee = Node(value: "coffee")
let coldBeverages = Node(value: "cold")
let water = Node(value: "water")
let soda = Node(value: "soda")
beverages.add(child: hotBeverages)
hotBeverages.add(child: coffee)
beverages.add(child: coldBeverages)
coldBeverages.add(child: water)
coldBeverages.add(child: soda)
Now I need to export this code to json. I'm passing it into webkit so I can load a hierarchal tree via D3. This means I need the export it so it matches the following format...
[
{
"name": "beverages",
"children": [
{
"name": "cold",
"children": [
{"name": "water"},
{"name": "soda"}
]},
{
"name": "warm",
"children": [
{"name": "coffee"}
]
}]
}
]
I've seen a lot of posts about exporting swift to basic json, but can't quite wrap my head around doing it with a tree like this. Any help would be appreciated!
Note: I don't need pretty printed json. That's just for readability here
You simply need to make Node conform to Encodable. The only tricky step you need to do is to only encode children in case it has any elements, otherwise only encode value. You also need to declare a CodingKey conformant enum to tell Encodable to encode the value property using the JSON key name rather than value.
extension Node: Encodable {
private enum CodingKeys: String, CodingKey {
case value = "name"
case children
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(value, forKey: .value)
if !children.isEmpty {
try container.encode(children,forKey: .children)
}
}
}
And then use it like this
do {
let encodedBevarages = try JSONEncoder().encode(beverages)
print(String(data: encodedBevarages, encoding: .utf8) ?? "Encoding failed")
} catch {
error
}
Output:
{"name":"beverages","children":[{"name":"hot","children":[{"name":"coffee"}]},{"name":"cold","children":[{"name":"water"},{"name":"soda"}]}]}

Deserializing JSON to KeyValuePairs<string, CustomClass> [duplicate]

I have a simple JSON object:
{
"values": {
"a":"",
"b":"",
"c":"",
"d":"",
"e":""
}
}
and I want to decode it to a Swift struct in such a way, that I can later be able to iterate over the keys in values in the exact same order as I receive the JSON object.
Is this possible in Swift?
My try below:
let json = "{ \"values\": { \"a\":\"\", \"b\":\"\", \"c\":\"\", \"d\":\"\", \"e\":\"\" } }"
struct JSS: Codable {
var values: [String: String?]?
}
let data = json.data(using: .utf8)
do {
let decoder = JSONDecoder()
let jss = try decoder.decode(JSS.self, from: data!)
jss.values?.map { print("\($0.key)") }
}
catch {
}
will print:
b
e
a
d
c
This is not a Swift limitation per se. Both Swift and JSON Dictionaries are unordered. The JSON format does not guarantee key ordering, and as such, does not require parsers to preserve the order.
If you need an ordered collection, you'd be better off with returning an array of key-value pairs in the JSON:
{
"values": [
{"a" : ""},
{"b" : ""},
{"c" : ""},
{"d" : ""},
{"e" : ""}
]
}
And then store the keys in the right order to be able to iterate over them as you wish.
The following does not work so you don't waste time trying:
Back to the old fashion way, use JSONSerialization.jsonObject(with: Data, options: JSONSerialization.ReadingOptions) -> Any.
It does keep order..... UNTIL you cast json?["values"] into a [String: Any]. At this time what Cezar says in the above answer enters the scene: dictionaries are unordered.
The following screenshot shows that, until json?["values"] is an Any, the order is kept in the string description.

Facing Problem in encoding array of dictionary to 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 !

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) }