import JSON in Realm - json

I have a SQL Database on Azure and I would like to synchronize it with Realm, for my iOS App (in Swift)
For that, I have created a REST API which generates a JSON and now I would like to integrate this JSON in Realm.
To do that, I have tried to follow the explanation on Realm Documentation, so now I have :
Realm Table :
class tbl_test: Object {
dynamic var id:Int = 0
dynamic var name:String = ""
override class func primaryKey() -> String? {
return "id"
}
}
Swift Code :
let realm = try! Realm()
let stringTxt:String = "[{\"id\": 1, \"name\": \"My Name\"}]"
var myData = NSData()
if let dataFromString = stringTxt.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
let jsonData = JSON(data: dataFromString)
if let encryptedData:NSData = try! jsonData.rawData() {
myData = encryptedData
}
}
try! realm.write {
let json = try! NSJSONSerialization.JSONObjectWithData(myData, options: NSJSONReadingOptions())
realm.create(tbl_test.self, value: json, update: true)
}
I use SwiftyJSON to convert my string to JSON.
When I run the program, I have this error message :
[__NSCFDictionary longLongValue]: unrecognized selector sent to
instance 0x7fdcc8785820 2016-07-06 10:25:30.090
mydrawing[9436:2732447] *** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSCFDictionary
longLongValue]: unrecognized selector sent to instance 0x7fdcc8785820'
Is it a good way to import JSON in Realm ? There is no official way according to what I have found, but this method should work...

The problem you're facing is that the structure of the data you're passing to Realm.create(_:value:update:) doesn't match what the method expects. It expects either a dictionary with keys corresponding to the managed properties on your model type, or an array with one element for each managed property.
After deserializing the JSON data, json looks like so:
(
{
id = 1;
name = "My Name";
}
)
That is an array containing a single element that is an dictionary. When you pass this array to Realm.create(_:value:update:), Realm expects the first element of the array to be the value to use as the id property on your tbl_test type.
I suspect that what you mean to do is to call Realm.create on each of the elements of the array in turn, instead of calling it on the array itself.

Related

Swift: inner Data-typed variable was encoded via JSONEncoder. How to deserialize it properly?

Consider the next example:
import Foundation
class UDFrame: Codable {
var data:Data
init(data:Data) {
self.data = data
}
}
class Event: Codable {
var name:String
init(name:String) {
self.name = name
}
}
let encoder = JSONEncoder()
let event = Event(name: "eventName")
let serializedEvent = try encoder.encode(event)
let frame = UDFrame(data: serializedEvent)
let serializedFrame = try encoder.encode(frame)
print(String(data: serializedFrame, encoding: String.Encoding.utf8)!)
Result of the print statement is next: {"data":"eyJuYW1lIjoiZXZlbnROYW1lIn0="}.
My question is how to get "eventName" out of this drivel?
And, if possible, could you please explain why Data is serialized in such way by JSONEncoder, and what's the way to get initial data on another platform when such a JSON is given?
You can simply use JSONDecoder for decoding a JSON-encoded Data.
Data is simply base64encoded, so you just need to base64 decode it on another platform to get back the original data. However, there's no need to store a JSON-encoded object as a property of another object, you can simply use the JSON-encoded object.

Vapor3 JSON encode & Decode

I'm having to use a FoundationDB database for a project. It stores data as a key:value pair stored as Bytes. I want to store a JSON object mapped to a struct I have. I want to be able save the data as an encoded JSON object and then be able to recreate the JSON object by reading the value Bytes from the DB.
In my CreateRecord function I pass a JSON in a request and use it to create my Country object. I need to convert the type Data into Bytes to store it.
So far I have come up with this.
let data: Bytes = try JSONEncoder().encode(country).base64URLEncodedString()
Then, when I read the record from the database I need to be able to reverse the process to create my Country object from the Bytes that store the JSON.
let mycountry:Country = try decoder.decode(Country.self, from: Data(bytes: DBrecord.value) )
The struct is:
struct Country: Content {
var country_name: String
var timezone: String
var default_PickUp_location: String = ""
init(country_name: String, timezone:String, default_PickUp_location: String?) {
self.country_name = country_name
self.timezone = timezone
if default_PickUp_location != nil {
self.default_PickUp_location = default_PickUp_location!
}
}
}
And a sample JSON is:
{ "country_name" : "Denmark", "timezone" : "Europe\/Copenhagen", "default_pickup_location" : "Copenhagen" }
I cannot seem to be able to reverse the conversion. Any help please?
In your JSON, you use default_pickup_location and in your struct you use default_PickUp_location. You need to settle on one version.
To discover this, I used the following test route:
router.get("json")
{
request throws -> String in
let json = "{ \"country_name\" : \"Denmark\", \"timezone\" : \"Europe/Copenhagen\", \"default_pickup_location\" : \"Copenhagen\" }"
let encoder = try JSONDecoder().decode(Country.self, from: json)
return "It works"
}
It returned:
{"error":true,"reason":"Value required for key
'default_PickUp_location'."}
Where is the type "Bytes" declared, I don't recognize it as a SwiftLang or Foundation Type. I do recoginize Data(bytes: Array<UInt>)? I would guess it is an encoding Decoding type inconsitency. Also for simplifying your encoding and decoding to JSON with out mangling Camel Case in swift you may want to try.
struct Country: Content {
let countryName: String
let timezone: String
let defaultPickupLocation: String
enum CodingKeys: String, CodingKey {
case countryName = "country_name"
case timezone
case defaultPickupLocation = "default_pickup_location"
}
}
I would further recommend that you grab then encoders and decoders from the container for reasons of type consistency. If for example all of your JSON was going to be snake case you could change the encoders and decoders for your container and skip the coding keys enum from above.
var encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
You can get the encoder and decoder from the container as follows
let contentCoders: ContentCoders = try worker.make()
let jsonEncoder = try contentCoders.requireDataEncoder(for: .json) as? JSONEncoder

How to get the data inside the json curly braces? Swift

I can get the result and message data but I can't get the crew_name inside the crew. How can I get the data if it is inside the curly braces?
Json printed on console
{
crew = {
"crew_avatar" = "http://ec2-52-221-231-3.ap-southeast-1.compute.amazonaws.com/gv/images/profile_image/Pang_Kang_Ming_916210_0e9.jpg";
"crew_contact" = 0123456789;
"crew_email" = "pang#xover.com.my";
"crew_gender" = Male;
"crew_id" = PP000001;
"crew_name" = "Pang Kang Ming";
"crew_preferred_name" = PKM;
"crew_qrcode" = "images/qrcode/qrcode_085960293a5378a64bec6ebfa3c89bb7.png";
};
message = "Login Sucessfully";
result = success;
}
#IBOutlet var empNameLabel: UILabel!
#IBOutlet var empIdLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let empNameValue = user!("crew"["crew_name"])as? String
let empIdValue = user!["result"]as? String
empNameLabel.text = empNameValue
empIdLabel.text = empNameValue
}
The code inside the curly braces is a dictionary, you can access that by first creating a dictionary from the "crew" key, and then accessing "crew_name" through that.
Assuming that the user variable is correctly created with the JSON data you can do the following to access "crew_name":
if let crew = user!["crew"] as? [String:Any], let crewName = crew["crew_name"] as? String {
print(crewName) // output: "Pang Kang Ming"
}
You need to deserialize your JSON in order to use the properties inside the object. Refer to this link where they use ObjectMapper to serialize and deserialize JSON Objects.
NOTE: Do read up on the documentation before using ObjectMapper
You need to deserialize your JSON to an object You can use Swift's build it JSON serialization with NSJSONSerialization which is a bit cumbersome or use open source library like SwiftyJSON
After you deserialize you will have an object that represents your JSON, then you can access it like you access any parameter of a class (if you will SwifyJSON), or you can access it like a dictionary
yourJSONObject["crew"]["crew_name"]

How to convert NSObject class object into JSON in Swift?

I have
var contacts : [ContactsModel] = []
and
class ContactsModel: NSObject
{
var contactEmail : String?
var contactName : String?
var contactNumber : String?
var recordId : Int32?
var modifiedDate : String?
}
Now in contacts I'm having 6 values like
Now i want to convert contacts into JSON how can i ?
I tried
var jsonData: NSData?
do
{
jsonData = try NSJSONSerialization.dataWithJSONObject(contacts, options:NSJSONWritingOptions.PrettyPrinted)
} catch
{
jsonData = nil
}
let jsonDataLength = "\(jsonData!.length)"
But it crashes the app.
My issue is with manually converting in to a Dictionary one by one very time consuming it takes more than 5 minutes for 6000 records, So instead of that i want to convert directly model into JSON and send to server.
Your custom object can't be converted to JSON directly. NSJSONSerialization Class Reference says:
An object that may be converted to JSON must have the following properties:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
You might convert your object to a Dictionary manually or use some libraries like SwiftyJSON, JSONModel or Mantle.
Edit: Currently, with swift 4.0+, you can use Codable protocol to easily convert your objects to JSON. It's a native solution, no third party library needed. See Using JSON with Custom Types document from Apple.
If you just want a simple Swift object to JSON static function without any inheritance or dependencies to NSObject or NS-types directly. Check out:
https://github.com/peheje/JsonSerializerSwift
Full disclaimer. I made it. Simple use:
//Arrange your model classes
class Object {
var id: Int = 182371823
}
class Animal: Object {
var weight: Double = 2.5
var age: Int = 2
var name: String? = "An animal"
}
class Cat: Animal {
var fur: Bool = true
}
let m = Cat()
//Act
let json = JSONSerializer.toJson(m)
Currently supports standard types, optional standard types, arrays, arrays of nullables standard types, array of custom classes, inheritance, composition of custom objects.
You can use NSJSONSerialization for array when array contains only JSON encodable values (string, number, dictionary, array, nil)
first you need to create the JSON object then you can use it (your code is crashing because contact is not a JSON object!)
you can refere below link
https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSJSONSerialization_Class/#//apple_ref/doc/uid/TP40010946-CH1-SW9

Automatic JSON serialization and deserialization of objects in Swift

I'm looking for a way to automatically serialize and deserialize class instances in Swift. Let's assume we have defined the following class …
class Person {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
… and Person instance:
let person = Person(firstName: "John", lastName: "Doe")
The JSON representation of person would be the following:
{
"firstName": "John",
"lastName": "Doe"
}
Now, here are my questions:
How can I serialize the person instance and get the above JSON without having to manually add all properties of the class to a dictionary which gets turned into JSON?
How can I deserialize the above JSON and get back an instantiated object that is statically typed to be of type Person? Again, I don't want to map the properties manually.
Here's how you'd do that in C# using Json.NET:
var person = new Person("John", "Doe");
string json = JsonConvert.SerializeObject(person);
// {"firstName":"John","lastName":"Doe"}
Person deserializedPerson = JsonConvert.DeserializeObject<Person>(json);
As shown in WWDC2017 # 24:48 (Swift 4), we will be able to use the Codable protocol. Example
public struct Person : Codable {
public let firstName:String
public let lastName:String
public let location:Location
}
To serialize
let payload: Data = try JSONEncoder().encode(person)
To deserialize
let anotherPerson = try JSONDecoder().decode(Person.self, from: payload)
Note that all properties must conform to the Codable protocol.
An alternative can be JSONCodable which is used by Swagger's code generator.
You could use EVReflection for that. You can use code like:
var person:Person = Person(json:jsonString)
or
var jsonString:String = person.toJsonString()
See the GitHub page for more detailed sample code. You only have to make EVObject the base class of your data objects. No mapping is needed (as long as the json keys are the same as the property names)
Update: Swift 4 has support for Codable which makes it almost as easy as EVReflection but with better performance. If you do want to use an easy contractor like above, then you could use this extension: Stuff/Codable
With Swift 4, you simply have to make your class conform to Codable (Encodable and Decodable protocols) in order to be able to perform JSON serialization and deserialization.
import Foundation
class Person: Codable {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
Usage #1 (encode a Person instance into a JSON string):
let person = Person(firstName: "John", lastName: "Doe")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted // if necessary
let data = try! encoder.encode(person)
let jsonString = String(data: data, encoding: .utf8)!
print(jsonString)
/*
prints:
{
"firstName" : "John",
"lastName" : "Doe"
}
*/
Usage #2 (decode a JSON string into a Person instance):
let jsonString = """
{
"firstName" : "John",
"lastName" : "Doe"
}
"""
let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let person = try! decoder.decode(Person.self, from: jsonData)
dump(person)
/*
prints:
▿ __lldb_expr_609.Person #0
- firstName: "John"
- lastName: "Doe"
*/
There is a Foundation class called NSJSONSerialization which can do conversion to and from JSON.
The method for converting from JSON to an object looks like this:
let jsonObject = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers,
error: &error) as NSDictionary
Note that the first argument to this method is the JSON data, but not as a string object, instead as a NSData object (which is how you'll often times get JSON data anyway).
You most likely will want a factory method for your class that takes JSON data as an argument, makes use of this method and returns an initialize object of your class.
To inverse this process and create JSON data out of an object, you'll want to make use of dataWithJSONObject, in which you'll pass an object that can be converted into JSON and have an NSData? returned. Again, you'll probably want to create a helper method that requires no arguments as an instance method of your class.
As far as I know, the easiest way to handle this is to create a way to map your objects properties into a dictionary and pass that dictionary for turning your object into JSON data. Then when turning your JSON data into the object, expect a dictionary to be returned and reverse the mapping process. There may be an easier way though.
You can achieve this by using ObjectMapper library. It'll give you more control on variable names and the values and the prepared JSON. After adding this library extend the Mappable class and define mapping(map: Map) function.
For example
class User: Mappable {
var id: Int?
var name: String?
required init?(_ map: Map) {
}
// Mapping code
func mapping(map: Map) {
name <- map["name"]
id <- map["id"]
}
}
Use it like below
let user = Mapper<User>().map(JSONString)
First, create a Swift object like this
struct Person {
var firstName: String?;
var lastName: String?;
init() {
}
}
After that, serialize your JSON data you retrieved, using the built-in NSJSONSerialization and parse the values into the Person object.
var person = Person();
var error: NSError?;
var response: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(), error: &error);
if let personDictionary = response as? NSDictionary {
person.firstName = personDictionary["firstName"] as? String;
person.lastName = personDictionary["lastName"] as? String;
}
UPDATE:
Also please take a look at those libraries
Swift-JsonSerialiser
ROJSONParser
Take a look at NSKeyValueCoding.h, specifically setValuesForKeysWithDictionary. Once you deserialize the json into a NSDictionary, you can then create and initialize your object with that dictionary instance, no need to manually set values on the object. This will give you an idea of how the deserialization could work with json, but you will soon find out you need more control over deserialization process. This is why I implement a category on NSObject which allows fully controlled NSObject initialization with a dictionary during json deserialization, it basically enriches the object even further than setValuesForKeysWithDictionary can do. I also have a protocol used by the json deserializer, which allows the object being deserialized to control certain aspects, for example, if deserializing an NSArray of objects, it will ask that object what is the type name of the objects stored in the array.