create a JSON using swift - ios

let nlp_json_string = "{\"appId\":\"**********\",\"cloud\":true}"
let cmd_json_string = "{\"asr\":\"\",\"id\":\(id),\"name\":\"\(name)\",\"nlp\":\"\(nlp_json_string)\",\"subid\":\(subid)}"
print(cmd_json_string)
log:
{"asr":"","id":260744,"name":"aaaaaa","nlp":"{"appId":"**********","cloud":true}","subid":123743947}
I want to build a JSON like this, but this JSON format is wrong, what should I do,The problem is that the "nlp" key causes the JSON to not be formatted. (note: the type of "nlp" value needs to be a String), thanks!

You can use the Encodable protocol to convert an object to JSON string or JSON object upto your choice.
Please try to understand the example
//This the the customer structure
struct Customer: Codable {
var name: String?
var id: String?
var account: Account?
}
//This is customer's account structure
struct Account: Codable {
var acId: String?
var openedDate: Date?
}
//Main View Controller.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Call this fnctions to initiate the process.
self.makeJson()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func makeJson() {
//Make the account object
let accountInfo = Account.init(acId: "100100234", openedDate: Date())
//Make the customer object
let customer = Customer.init(name: "Naresh", id: "dfg-2561", account: accountInfo)
do {
//make data object
let data = try JSONEncoder.init().encode(customer)
//print as JSON String
let jsonString = String.init(data: data, encoding: String.Encoding.utf8)
print(jsonString ?? "Not parsed")
//print as JSON Object
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
print(jsonObject)
}
catch {
print(error.localizedDescription)
}
}
}
Look at the terminal. This will print the json string & json object both.

The easy way is to create a multiline String:
let myJSON: String = """
{
"id" : 12,
"foo": "bar"
}
"""

First of all take advantage of the new literal string syntax to avoid the confusion of the escaped double quotes.
Second of all please conform to the naming convention that variables are camelCased
Assumed variables :
let name = "Foo"
let id = 1
let subid = 2
The strings look much clearer.
let nlpJsonString = """
{"appId":"**********","cloud":true}
"""
let cmdJsonString = """
{"asr":"","id":\(id),"name":"\(name)","nlp":"\(nlpJsonString)","subid":\(subid)}
"""
As you can see (better now) nlpJsonString is inserted as String (note the wrapping double quotes) so the result is
{"asr":"","id":1,"name":"Foo","nlp":"{"appId":"**********","cloud":true}","subid":2}
If you want to insert nlpJsonString as a dictionary remove the double quotes
let cmdJsonString = """
{"asr":"","id":\(id),"name":"\(name)","nlp":\(nlpJsonString),"subid":\(subid)}
"""
then you get
{"asr":"","id":1,"name":"Foo","nlp":{"appId":"**********","cloud":true},"subid":2}
An alternative is to create dictionaries and serialize them
let name = "Foo"
let id = 1
let subid = 2
let nlpDict : [String:Any] = ["appId":"**********","cloud":true]
let cmdDict : [String:Any] = ["asr":"","id":id,"name":name,"nlp":nlpDict,"subid":subid]
do {
let jsonData = try JSONSerialization.data(withJSONObject: cmdDict)
let json = String(data: jsonData, encoding: .utf8)!
print(json)
} catch { print("something went wrong", error) }

Related

Separating key values from a string in Swift that looks like an object array

If I do this:
let retrievedJson = String(decoding: data, as: UTF8.self)
print(retrievedJson)
I get this in the console:
[{"label":"20-05-16","value":"67p4i"},{"label":"20-05-17","value":"d17bz"}]
How do I take this string and make two separate arrays.
ex:
["20-05-16", "20-05-17"]
["67p4i", "d17bz"]
Is there also a method to convert a String looking like an array to an array?
You need
do {
let res = try JSONDecoder().decode([Root].self,from:data)
let keys = res.map { $0.label } // ["20-05-16", "20-05-17"]
let values = res.map { $0.value } // ["67p4i", "d17bz"]
}
catch {
print(error)
}
struct Root: Codable {
let label, value: String
}

Swift JSON response two field values append into single array

I am trying to validated students == null or values avilable, If values avilable I need to get grade and store grade into table data array and subject null also I need to store in same array For example: [10, null, 11] from below JSON. how to append like this in single array from JSON response.
{
"students":[
{
"id":0,
"subject":[
{
"grade":10
}
]
},
{
"id":1,
"subject":null
},
{
"id":2,
"subject":[
{
"grade":11
}
]
}
]
}
Expected output: [10,null,11,......] //This array I am going to use Tableview cell
I am validating based on null and not null array values within cell for row. I can use var array = [String?] for accepting null values but how to append two different field result into same array?
You should take a look into the 'Codable' protocol.
By simply defining a struct like:
struct Student: Codable
you can decode it from JSON into these objects.
See for example: hackernoon or grokswift
This looks like a trivial scenario. Best solution is Decodable. Your payload loaded from network or whatever will be parsed into structure. Now you can easily make any manipulations.
Setup: Open a new project. Add "payload.json" file with json payload you provided in question.
Add the following to your project.
import UIKit
struct StudentData: Decodable {
var students: [Student]
}
struct Student: Decodable {
var id: Int
var subject: [Subject]?
}
struct Subject: Decodable {
var grade: Int
}
class ViewController: UIViewController {
var data: Data? {
guard let path = Bundle(for: type(of: self)).path(forResource: "payload", ofType: "json") else { return nil }
return try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
}
override func viewDidLoad() {
super.viewDidLoad()
if let data = data {
do {
let studentData = try JSONDecoder().decode(StudentData.self, from: data)
print(studentData)
// manipulate the structure in any way you want
let subjects: [Subject?] = studentData.students.map { $0.subject?.first }
print(subjects)
let nonNilValues = subjects.compactMap { $0 }
print(subjects)
// ... etc
} catch let error {
print(error.localizedDescription)
}
}
}
}
Sorry for not coding in playgrounds. It's way too buggy.
Try this
let students = [["id": 0,"subject": [["grade": 10]]],
["id": 0,"subject": nil],
["id": 0,"subject": [["grade": 10]]]] as! [Dictionary<String,Any>]
let array = students.map({(($0["subject"] as? [Any])?.first as? Dictionary<String,Int>)?["grade"]})
print(array)

Turn swift object into a JSON string

I have classes like these:
class MyDate
{
var year : String = ""
var month : String = ""
var day : String = ""
init(year : String , month : String , day : String) {
self.year = year
self.month = month
self.day = day
}
}
class Lad
{
var firstName : String = ""
var lastName : String = ""
var dateOfBirth : MyDate?
init(firstname : String , lastname : String , dateofbirth : MyDate) {
self.firstName = firstname
self.lastName = lastname
self.dateOfBirth = dateofbirth
}
}
class MainCon {
func sendData() {
let myDate = MyDate(year: "1901", month: "4", day: "30")
let obj = Lad(firstname: "Markoff", lastname: "Chaney", dateofbirth: myDate)
let api = ApiService()
api.postDataToTheServer(led: obj)
}
}
class ApiService {
func postDataToTheServer(led : Lad) {
// here i need to json
}
}
And I would like to turn a Lad object into a JSON string like this:
{
"firstName":"Markoff",
"lastName":"Chaney",
"dateOfBirth":
{
"year":"1901",
"month":"4",
"day":"30"
}
}
EDIT - 10/31/2017: This answer mostly applies to Swift 3 and possibly earlier versions. As of late 2017, we now have Swift 4 and you should be using the Encodable and Decodable protocols to convert data between representations including JSON and file encodings. (You can add the Codable protocol to use both encoding and decoding)
The usual solution for working with JSON in Swift is to use dictionaries. So you could do:
extension Date {
var dataDictionary {
return [
"year": self.year,
"month": self.month,
"day": self.day
];
}
}
extension Lad {
var dataDictionary {
return [
"firstName": self.firstName,
"lastName": self.lastName,
"dateOfBirth": self.dateOfBirth.dataDictionary
];
}
}
and then serialize the dictionary-formatted data using JSONSerialization.
//someLad is a Lad object
do {
// encoding dictionary data to JSON
let jsonData = try JSONSerialization.data(withJSONObject: someLad.dataDictionary,
options: .prettyPrinted)
// decoding JSON to Swift object
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// after decoding, "decoded" is of type `Any?`, so it can't be used
// we must check for nil and cast it to the right type
if let dataFromJSON = decoded as? [String: Any] {
// use dataFromJSON
}
} catch {
// handle conversion errors
}
If you just need to do this for few classes, providing methods to turn them into dictionaries is the most readable option and won't make your app noticeably larger.
However, if you need to turn a lot of different classes into JSON it would be tedious to write out how to turn each class into a dictionary. So it would be useful to use some sort of reflection API in order to be able to list out the properties of an object. The most stable option seems to be EVReflection. Using EVReflection, for each class we want to turn into json we can do:
extension SomeClass: EVReflectable { }
let someObject: SomeClass = SomeClass();
let someObjectDictionary = someObject.toDictionary();
and then, just like before, we can serialize the dictionary we just obtained to JSON using JSONSerialization. We'll just need to use object.toDictionary() instead of object.dataDictionary.
If you don't want to use EVReflection, you can implement reflection (the ability to see which fields an object has and iterate over them) yourself by using the Mirror class. There's an explanation of how to use Mirror for this purpose here.
So, having defined either a .dataDictionary computed variable or using EVReflection's .toDictionary() method, we can do
class ApiService {
func postDataToTheServer(lad: Lad) {
//if using a custom method
let dict = lad.dataDictionary
//if using EVReflection
let dict = lad.toDictionary()
//now, we turn it into JSON
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict,
options: .prettyPrinted)
// send jsonData to server
} catch {
// handle errors
}
}
}
May this GitHub code will help you.
protocol SwiftJsonMappable {
func getDictionary() -> [String: Any]
func JSONString() -> String
}
extension SwiftJsonMappable {
//Convert the Swift dictionary to JSON String
func JSONString() -> String {
do {
let jsonData = try JSONSerialization.data(withJSONObject: self.getDictionary(), options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let jsonString = String(data: jsonData, encoding: .utf8) ?? ""
// here "decoded" is of type `Any`, decoded from JSON data
return jsonString
// you can now cast it with the right type
} catch {
print(error.localizedDescription)
}
return ""
}
//Convert Swift object to Swift Dictionary
func getDictionary() -> [String: Any] {
var request : [String : Any] = [:]
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let lable = child.label {
//For Nil value found for any swift propery, that property should be skipped. if you wanna print nil on json, disable the below condition
if !checkAnyContainsNil(object: child.value) {
//Check whether is custom swift class
if isCustomType(object: child.value) {
//Checking whether its an array of custom objects
if isArrayType(object: child.value) {
if let objects = child.value as? [AMSwiftBase] {
var decodeObjects : [[String:Any]] = []
for object in objects {
//If its a custom object, nested conversion of swift object to Swift Dictionary
decodeObjects.append(object.getDictionary())
}
request[lable] = decodeObjects
}
}else {
//Not an arry, and custom swift object, convert it to swift Dictionary
request[lable] = (child.value as! AMSwiftBase).getDictionary()
}
}else {
request[lable] = child.value
}
}
}
}
return request
}
//Checking the swift object is swift base type or custom Swift object
private func isCustomType(object : Any) -> Bool {
let typeString = String(describing: type(of: object))
if typeString.contains("String") || typeString.contains("Double") || typeString.contains("Bool") {
return false
}
return true
}
//checking array
private func isArrayType(object : Any) -> Bool {
let typeString = String(describing: type(of: object))
if typeString.contains("Array"){
return true
}
return false
}
//Checking nil object
private func checkAnyContainsNil(object : Any) -> Bool {
let value = "\(object)"
if value == "nil" {
return true
}
return false
}
}
https://github.com/anumothuR/SwifttoJson

Why array returns nil after appending to it elements?

I'm trying to work with JSON as MVC model, for this I did:
// Country.swift
import SwiftyJSON
class Country {
var code: String!
var dialCode: Int!
var name: String!
init(json: JSON) {
for i in 0...json["countries"].count - 1 {
if let code = json["countries"][i]["code2"].string, dialCode = json["countries"][i]["dialCode"].string, name = json["countries"][i]["name"].string {
self.code = code
self.dialCode = Int(dialCode)
self.name = name
}
}
}
}
and later in my ViewController I do:
var countries = [Country]()
Alamofire.request(.POST, "\(property.host)\(property.getCountryList)", parameters: parameters, encoding: .JSON).responseJSON { response in
do {
let json = JSON(data: response.data!)
countries.append(Country(json: json))
} catch _ {
}
}
but I have a problem. When I print values in Country.swift file, I get results, but when I print(countries) it returns me [Project.Country] and count returns 1. What is the problem? What I do wrong?
Unless I've misunderstood is this not your desired behaviour?
countries is an array of Project.Country, which swift is representing by printing [Project.Country] (an array containing one instance of your class). There is no issue. If you want to prove that the array contains a Project.Country you should print one of the class' properties: print(countries.first.name)
EDIT: problem is you are passing a JSON array of countries to a single init method, which is just setting the properties of itself for every country and not creating an instance for each. Hence you only have one instance returned
Your problem is you are passing countries array to init method which is called only once you must do it like here
class Country {
var code: String!
var dialCode: Int!
var name: String!
init(json: JSON) {
if let code = json["code2"].string, dialCode = json["dialCode"].string, name = json["name"].string {
self.code = code
self.dialCode = Int(dialCode)
self.name = name
}
}
}
And loop here
Alamofire.request(.POST, "", parameters: nil, encoding: .JSON).responseJSON { response in
if let jsonResponse = response.result.value{
let json = JSON(jsonResponse)
for countriesJSON in json["countries"].arrayValue{
self.countries.append(Country(json: countriesJSON))
}
print(self.countries.count)
}
}

How can I store a Dictionary with RealmSwift?

Considering the following model:
class Person: Object {
dynamic var name = ""
let hobbies = Dictionary<String, String>()
}
I'm trying to stock in Realm an object of type [String:String] that I got from an Alamofire request but can't since hobbies has to to be defined through let according to RealmSwift Documentation since it is a List<T>/Dictionary<T,U> kind of type.
let hobbiesToStore: [String:String]
// populate hobbiestoStore
let person = Person()
person.hobbies = hobbiesToStore
I also tried to redefine init() but always ended up with a fatal error or else.
How can I simply copy or initialize a Dictionary in RealSwift?
Am I missing something trivial here?
Dictionary is not supported as property type in Realm.
You'd need to introduce a new class, whose objects describe each a key-value-pair and to-many relationship to that as seen below:
class Person: Object {
dynamic var name = ""
let hobbies = List<Hobby>()
}
class Hobby: Object {
dynamic var name = ""
dynamic var descriptionText = ""
}
For deserialization, you'd need to map your dictionary structure in your JSON to Hobby objects and assign the key and value to the appropriate property.
I am currently emulating this by exposing an ignored Dictionary property on my model, backed by a private, persisted NSData which encapsulates a JSON representation of the dictionary:
class Model: Object {
private dynamic var dictionaryData: NSData?
var dictionary: [String: String] {
get {
guard let dictionaryData = dictionaryData else {
return [String: String]()
}
do {
let dict = try NSJSONSerialization.JSONObjectWithData(dictionaryData, options: []) as? [String: String]
return dict!
} catch {
return [String: String]()
}
}
set {
do {
let data = try NSJSONSerialization.dataWithJSONObject(newValue, options: [])
dictionaryData = data
} catch {
dictionaryData = nil
}
}
}
override static func ignoredProperties() -> [String] {
return ["dictionary"]
}
}
It might not be the most efficient way but it allows me to keep using Unbox to quickly and easily map the incoming JSON data to my local Realm model.
I would save the dictionary as JSON string in Realm. Then retrive the JSON and convert to dictionary. Use below extensions.
extension String{
func dictionaryValue() -> [String: AnyObject]
{
if let data = self.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]
return json!
} catch {
print("Error converting to JSON")
}
}
return NSDictionary() as! [String : AnyObject]
} }
and
extension NSDictionary{
func JsonString() -> String
{
do{
let jsonData: Data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
return String.init(data: jsonData, encoding: .utf8)!
}
catch
{
return "error converting"
}
}
}
UPDATE 2021
Since Realm 10.8.0, it is possible to store a dictionary in a Realm object using the Map type.
Example from the official documentation:
class Dog: Object {
#objc dynamic var name = ""
#objc dynamic var currentCity = ""
// Map of city name -> favorite park in that city
let favoriteParksByCity = Map<String, String>()
}
Perhaps a little inefficient, but works for me (example dictionary from Int->String, analogous for your example):
class DictObj: Object {
var dict : [Int:String] {
get {
if _keys.isEmpty {return [:]} // Empty dict = default; change to other if desired
else {
var ret : [Int:String] = [:];
Array(0..<(_keys.count)).map{ ret[_keys[$0].val] = _values[$0].val };
return ret;
}
}
set {
_keys.removeAll()
_values.removeAll()
_keys.appendContentsOf(newValue.keys.map({ IntObj(value: [$0]) }))
_values.appendContentsOf(newValue.values.map({ StringObj(value: [$0]) }))
}
}
var _keys = List<IntObj>();
var _values = List<StringObj>();
override static func ignoredProperties() -> [String] {
return ["dict"];
}
}
Realm can't store a List of Strings/Ints because these aren't objects, so make "fake objects":
class IntObj: Object {
dynamic var val : Int = 0;
}
class StringObj: Object {
dynamic var val : String = "";
}
Inspired by another answer here on stack overflow for storing arrays similarly (post is eluding me currently)...

Resources