This question already has an answer here:
How to Make JSON from Array of Struct in Swift 3?
(1 answer)
Closed 6 years ago.
My model is
public class getCompanyRequestModel
{
public var userLoginId : String?
public var searchString : String?
public var tableName : String?
}
in viewController's viewdidload method, i created object of getCompanyRequestModel
var objGetCompany = getCompanyRequestModel()
objGetCompany.userLoginId = "Dilip";
objGetCompany.searchString = "tata";
objGetCompany.tableName = "null";
now i wanna to get JSON string from objGetCompany same as below
"{\"UserLoginId\":\"Dilip\",\"SearchString\":\"tata\",\"TableName\":\"null\"}";
in C# i get the JSON String from below line of code
string JsonParameters = JsonConvert.SerializeObject(objGetCompany);
Any one interested to solve my issue :-)
Add a property jsonRepresentation to the class:
var jsonRepresentation : String {
let dict = ["userLoginId" : userLoginId, "searchString" : searchString, "tableName" : tableName]
let data = try! JSONSerialization.data(withJSONObject: dict, options: [])
return String(data:data, encoding:.utf8)!
}
By the way: Class names in Swift are supposed to start with a capital letter.
And don't use optionals as a laziness alibi not to write initializers...
Update:
In Swift 4 there is a smarter way: Remove jsonRepresentation and adopt Codable
public class CompanyRequestModel : Codable { ...
then use JSONEncoder
let data = try JSONEncoder().encode(objGetCompany)
let jsonString = String(data: data, encoding: .utf8)!
let text = "{\"UserLoginId\":\"Dilip\",\"SearchString\":\"tata\",\"TableName\":\"null\"}";
if let data = text.data(using: .utf8) {
do {
let dictData = try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
let company = getCompanyRequestModel()
company.userLoginId = dictData["UserLoginId"] as? String
company.searchString = dictData["SearchString"] as? String
company.tableName = dictData["TableName"] as? String
} catch {
print(error.localizedDescription)
}
}
Related
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) }
After JSONSerialization decoded and as! cast to NSDictionary get data below.
{
language = {
A00001 = {
chineseSimplified = CIMB;
chineseTraditional = CIMB;
english = CIMB;
japanese = CIMB;
korean = CIMB;
};
};
}
How to access into nested NSDictionary?
I'm able to get one layer data through data["language"], somehow I can't access multiple layers like:
data["language"]["A00001"]["english"]
data["language"]?["A00001"]?["english"]?
data["language"]!["A00001"]!["english"]!
Xcode returns this error:
Type 'Any' has no subscript members
Reference similar question:
How to access deeply nested dictionaries in Swift
Accessing Nested NSDictionary values in Swift 3.0
How do I manipulate nested dictionaries in Swift, e.g. JSON data?
Try casting it into [String: [String: [String: String]]], which is a nested structure for Swift Dictionary.
let json = """
{
"language": {
"A00001": {
"chineseSimplified" : "CIMB",
"chineseTraditional" : "CIMB",
"english" : "CIMB",
"japanese" : "CIMB",
"korean" : "CIMB"
}
}
}
"""
if let jsonD = json.data(using: String.Encoding.utf8) {
do {
if let data = try JSONSerialization.jsonObject(with: jsonD, options: JSONSerialization.ReadingOptions.mutableLeaves) as? [String: [String: [String: String]]] {
print(data["language"]!["A00001"]!["english"]!)
}
} catch let error {
print(error.localizedDescription)
}
}
Here is another answer using Swift 4's Codable API. I thinks it's worth trying if your code needs type safety and you are sure the fields in JSON are consistent.
let json = """
{
"language": {
"A00001": {
"chineseSimplified" : "CIMB",
"chineseTraditional" : "CIMB",
"english" : "CIMB",
"japanese" : "CIMB",
"korean" : "CIMB"
}
}
}
"""
struct MyLanguages:Codable {
struct LanguageGroup: Codable {
struct Language:Codable {
var chineseSimplified: String
var chineseTraditional: String
var english: String
var japanese: String
var korean: String
}
var A00001: Language
}
var language: LanguageGroup
}
if let jsonD = json.data(using: String.Encoding.utf8) {
let jsonDecoder = JSONDecoder()
do {
let myLang = try jsonDecoder.decode(MyLanguages.self, from: jsonD)
print(myLang.language.A00001.chineseSimplified)
}
catch let err {
print(err.localizedDescription)
}
}
Here I am having value in JSON in which for some of multiple key value pairs it returning string and for some it is returning array here in custom attributes array in first dictionary in that value key value pair the data present is different and in the second dictionary value key value pair is different here then how to implement the model class for inside array for different key values ?
struct MediaGallery {
let id : Int
let mediaType : String
let label : Any
let position : Int
let disabled : Any
let file : String
init(dict : [String:Any]) {
self.id = (dict["id"] as? Int)!
self.mediaType = (dict["media_type"] as? String)!
self.label = dict["label"]!
self.position = (dict["position"] as? Int)!
self.disabled = dict["disabled"]!
self.file = (dict["file"] as? String)!
}
}
struct AttributeList {
let label : String
let value : String
let code : String
init(dict : [String:Any]){
self.label = (dict["label"])! as! String
self.value = (dict["value"])! as! String
self.code = (dict["code"])! as! String
}
}
struct DetailsListAttribute {
let attributeCode : String
let value : Any
init?(dict : [String:Any]) {
self.attributeCode = dict["attribute_code"] as! String
print(self.attributeCode)
if let values = dict["value"] as? String {
self.value = values
}
else {
if let arr = dict["value"] as? [[String:Any]]{
var filterArr = [AttributeList]()
for obj in arr {
filterArr.append(AttributeList(dict: obj))
}
self.value = filterArr
} else {
self.value = [AttributeList]()
}
}
}
}
I would suggest please save some time by using this great GIT Library ObjectMapper . it will help you to model your object and convert your model objects (classes and structs) to JSON and vice versa.
I've tried multiple JSON-mapping frameworks that were mentioned in Tj3n comment. They all have pros and cons. Apple suggests you to follow the recommendation given here. Also you should check Codable protocol (swift 4 is required).
Ok I don't have the whole JSON, and it doesn't seem clear to me.
But here is how you can parse and create your model Class easily in Swift with the Codable protocol.
You can read more about it and/or some examples, tutorials : Ultimate Guide.
Briefly, what is the Codable protocol ?
You don't need third party library anymore in order to parse and set the json data to your model class.
You juste have to create your class like the JSON is represented. And according to the key-name, it will create the class, properties and everything for you.
Here is an example with your JSON, I don't know if I understood your JSON formatting, but you got the trick :
struct Response: Codable {
let ca: [CustomAttribute]?
enum CodingKeys: String, CodingKey {
case ca = "custom_attributes"
}
}
struct CustomAttribute: Codable {
let code: String?
let value: [Value]?
struct Value: Codable {
let label: String?
let value: String?
let code: String?
let avg: String? // I don't know how your value array is composed
let count: Int? // I don't know how your value array is composed
}
enum CodingKeys: String, CodingKey {
case code = "attribute_code"
case avg = "avg_rating_percent"
}
}
For me, it looks like something like that.
I don't see the whole JSON, but imagine you have the whole JSON as the Response Struct, it contains several objects, like the CustomAttribute Array for example.
Then you can define the CustomAttribute structure, and add as many properties as the JSON has.
Anyway, you can call it this way :
When you have the response from your API call, you can go :
if let data = response.data {
let decoder = JSONDecoder()
let response = try! decoder.decode(Response.self, from: data)
print("Only printing the Custom Attribute : \(response.ca!)")
}
I decode the whole json data as an Object Response (like my Struct).
And I pass to my response callback, or
this might be late but I think this will helps others
The model class which are varies for frameworks like SwiftyJSON, simple swift class, Gloss or swift codable (Swift 4). you can easily generate model class online with your customization jsoncafe.com
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
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)...