I want to save json data into a file without being archived. This way I can open the file and check if everything is okay
// valid json data
if NSJSONSerialization.isValidJSONObject(jsonData) {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let documentsDirectory = paths[0] as! String
let path = documentsDirectory.stringByAppendingPathComponent("test.json")
println(path) // if I open the file from finder, all the data is archived
// try 1: the jsonData is archived
if NSKeyedArchiver.archiveRootObject(jsonData, toFile: path) {
println("saved: true");
}
let stringTest = "asd"
stringTest.writeToFile(path, atomically: true, encoding: NSStringEncoding.allZeros, error: nil)
// the string is also archived
}
I also try
var jsonNSData = NSKeyedArchiver.archivedDataWithRootObject(jsonData)
var string = NSString(data: jsonNSData, encoding: NSUTF8StringEncoding)
println(string) // returns nil
You can do it by casting your JSON to NSDictionary and then just write it to user documents directory:
var someJsonAsDict: [String: AnyObject] = ["foo": "string", "bar": NSNumber(integer: 5)]
if let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true) as? [String],
let documentsDirectory = paths.first
{
let filePath = documentsDirectory.stringByAppendingPathComponent("myPlistFile")
let fileManager = NSFileManager.defaultManager()
//IMPORTANT - someJsonAsDict cannot have primite types(they must be boxed in Appropriate NSNumber, NSValue, etc)
(someJsonAsDict as NSDictionary).writeToFile(filePath, atomically: true)
//print path of simulator so you can read it from terminal
println("target path is \(filePath)")
}
If you happen to have Array as most outer object of JSON, just replace:
someJsonAsDict as NSDictionary
with appropriate array method:
someJsonAsDict as NSArray
All this will create plist file with the JSON. If you happen to need it in raw json style(as like txt) you can use plutil(installed with Xcode command line tools):
plutil -convert json myPlistFile.plist -o MyTxtFile.json
maybe you can use NSUserDefaults to archive value. like var save = NSUserdefaults.userdefaults()then var ValueSaved = save.setObject(stringTest, "StringTest") this is the easiest mode to archive a file.. but it doesn't archive in a file :\
Related
I am trying to read a JSON file from myFile.json in Bundle, modify some elements and store it in the document directory as a JSON file. I thought that it would be a simple way to persist the data. Subsequently, I intend to read and write to document directory. The following code shows what I did and is heavily commented. It seems I am missing some essential step because the expected JSON conversions does not match the JSON spec. I am open to suggestions on how to I tested in the playground. The code is based on
Convert Dictionary to JSON in Swift
import UIKit
/* Trying to read a json file from myFile.json in Bundle, modify some elemnts and store it
in the document directory as a json file. Subsequently, intent to read and write to document directory
myFile.json consistes of
{
"record": {"id": "A1234", "customer": "Customer-1"}
}
*/
typealias MyRecord = [String: AnyObject]
var json:MyRecord!
let fileName = "myFile"
var dictionary = MyRecord()
func loadJsonFromBundle (forFilename fileName: String) -> MyRecord {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
if let data = NSData(contentsOf: url) {
do {
let dictionary = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as? [String:Any]
print("dictionary = \(dictionary!)")
/* displays
dictionary = ["record": {
customer = "Customer-1";
id = A1234;
}]
*/
return dictionary! as MyRecord
} catch {
print("Error!! Unable to parse \(fileName).json")
}
}
print("Error!! Unable to load \(fileName).json")
}
return [:]
}
func loadJsonFromDocument (forFilename fileName: String) -> MyRecord {
let docDirectory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
if let url = docDirectory?.appendingPathComponent(fileName).appendingPathExtension("json") {
if let data = NSData(contentsOf: url) {
do {
let dictionary = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as? [String:Any]
print("dictionary = \(dictionary!)")
return dictionary! as MyRecord
} catch {
print("Error!! Unable to parse \(fileName).json")
}
}
print("Error!! Unable to load \(fileName).json")
}
return [:]
}
func saveJsonToFile (_ fileName:String, outString: String) -> URL {
let docDirectory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
if let fileURL = docDirectory?.appendingPathComponent(fileName).appendingPathExtension("json") {
print("fileURL = \(fileURL)")
// Write to a file on disk
do {
try outString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
}
return fileURL
}
return URL(string: "")!
}
let sq = "\""
func q(_ x:String) -> String {
return "\(sq)\(x)\(sq)"
}
dictionary = loadJsonFromBundle (forFilename: fileName)
var a = dictionary["record"] as? [String:String]
a?["customer"] = "newName"
var dict = MyRecord()
dict["record"] = a as AnyObject?
print(dict)
/* prints:
["record": {
customer = newName;
id = A1234;
}]
*/
// https://stackoverflow.com/questions/29625133/convert-dictionary-to-json-in-swift/29628000#29628000
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// here "decoded" is of type `Any`, decoded from JSON data
// you can now cast it with the right type
if let dictFromJSON = decoded as? [String:Any] {
// need to save dictFromJson to a file in document directory
// saveJsonToFile is expecting a String for the json argument
// I converted dictFromJson to a string so I can save it
var outString = String(describing: dictFromJSON)
print("outString = \(outString)")
/* Notice that the internal structure is not quoted and there are semi-colons
outString = ["record": {
customer = newName;
id = A1234;
}]
*/
outString = outString.replacingOccurrences(of: "[", with: "{")
outString = outString.replacingOccurrences(of: "]", with: "}")
let url = saveJsonToFile("newFile", outString: String(describing: outString) )
print(url)
/* Resulting File looks like this:
{"record": {
customer = newName;
id = A1234;
}}
Question: Why were the braces swapped with brackets. The internal elements
were not quoted.
*/
// Will try to read back the json string from document directory
dictionary = loadJsonFromDocument(forFilename: fileName)
// results in ERROR (Unable to load myFile.json
a = dictionary["record"] as? [String:String]
a?["customer"] = "newName"
dict = MyRecord()
dict["record"] = a as AnyObject?
print(dict)
}
} catch {
print(error.localizedDescription)
}
Issue pointed by vadian is correct that you are storing the Dictionary object, but instead of converting Data to String and then write that String you can directly write Data in DocumentDirectory.
So i have changed your saveJsonToFile function which accept Data as second argument instead of String.
func saveJsonToFile (_ fileName:String, jsonData: Data) -> URL {
let docDirectory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
if let fileURL = docDirectory?.appendingPathComponent(fileName).appendingPathExtension("json") {
print("fileURL = \(fileURL)")
// Write to a file on disk
do {
try jsonData.write(to: fileURL)
} catch {
print("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
}
return fileURL
}
return URL(string: "")!
}
Now simply call this function after you change your json result and convert that to data.
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
let url = saveJsonToFile("newFile", jsonData: jsonData )
print(url)
} catch {
print(error.localizedDescription)
}
The issue is that you save a dictionary object to file rather than encoded JSON.
Use only the JSONSerialization from object to data and do not pass .prettyprinted.
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options:[])
// here "jsonData" is the dictionary encoded in JSON data
let outString = String(data:jsonData, encoding:.utf8)
print("outString = \(outString)")
let url = saveJsonToFile("newFile", outString: outString )
print(url)
}
} catch {
print(error.localizedDescription)
}
I have a JSON-NSArray (created with NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)...), containing a few items and want to save every item in it as a single file on my local disk.
I've wrote the following function to save a local json-file:
func writeLocalFileFromString(dataString dataString: String, path: String, filename: String, type: String) {
do {
try dataString.writeToFile("\(path)/\(filename).\(type)", atomically: true, encoding: NSUTF8StringEncoding)
} catch let error {
print(error)
}
}
So I tried to cast every json-item as! String. The files are created and they're containing a good-looking string, but however, they're not valid json-files. Is there a better way to handle this?
You loop and get every item in json NSDictionary, then convert this item json format. Instead of print it as the code block below, you can save it to file as you want.
let json = try! NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers) as! NSDictionary
// loop to get item
for (key, value) in json {
// data is a NSData contains your json string
let data = try! NSJSONSerialization.dataWithJSONObject([key, value], options: [])
// convert NSData to String, then do what you want
let string = String(data: data, encoding: NSUTF8StringEncoding)
print(string)
}
I'm new to Swift - trying to read a JSON file from a URL. My attempt below.
The JSON looks valid - I tested it with JSONLint but it keeps crashing.
Thoughts?
func getRemoteJsonFile() -> NSDictionary {
//Create a new url
let remoteUrl:NSURL? = NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")
//check if its nil
if let actualRemoteUrl = remoteUrl {
//try to get the data
let filedata:NSData? = NSData(contentsOfURL: actualRemoteUrl)
//check if its nil
if let actualFileData = filedata {
//parse out the dictionaries
let jsonDict = NSJSONSerialization.JSONObjectWithData(actualFileData, options: NSJSONReadingOptions.AllowFragments, error: nil) as NSDictionary
return jsonDict
}
}
return NSDictionary()
}
This took me a second to figure out, so I don't blame you for missing it.
The JSON you linked to is minified, so it's difficult to see the structure. Let's take a look at (a fragment of) it after piping it through a prettifier:
[
{
"PlayerId":2501863,
"PlayerName":"Peyton Manning",
"PlayerTeam":"DEN",
"PlayerPosition":"QB",
"PlayerPassingYards":4727,
"PlayerPassingTDs":39,
"PlayerInterceptions":15,
"PlayerRushingYards":-24,
"PlayerRushingTDs":0,
"PlayerReceivingYards":0,
"PlayerReceivingTDs":0,
"PlayerReturnYards":0,
"PlayerReturnTDs":0,
"PlayerFumbleTDs":0,
"PlayerTwoPointConversions":2,
"PlayerFumblesLost":2,
"PlayerTeamLogo":"http://i.nflcdn.com/static/site/7.0/img/logos/teams-gloss-81x54/den.png"
}
]
Huh. It's encased in brackets, which means that it's an array.
It's an array, so you can't cast it as an NSDictionary. Instead, you could cast it as an NSArray, but why not use native Swift types?
Well, if you don't like types, you're about to find out, but I still think that this is a better way, because it forces you to think about the data you're parsing.
So we have the first part of our type definition for this function; it's an array ([]). What components is our array made up of? We could go with a simple NSDictionary, but we're doing full native types here, so let's use a native Swift dictionary.
To do that, we have to know the types of the dictionary (the syntax for a native dictionary type is [KeyType: ValueType]). Examining the JSON shows that all of the keys are Strings, but the values are of varying types, so we can use AnyObject.
That gives us a dictionary type of [String: AnyObject], and our entire JSON is an array of that, so the final type is [[String: AnyObject]] (wow).
Now that we have the proper type, we can modify the function you're using to parse the JSON a bit.
First of all, let's use our new type for the return and cast values. Then, let's make the return type optional in case something goes wrong and add an error variable to document that.
A cleaned up function would look something like this:
func getData() -> [[String: AnyObject]]? {
let data: NSData? = NSData(contentsOfURL: NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")!)
if let req: NSData = data {
var error: NSError?
if let JSON: [[String: AnyObject]] = NSJSONSerialization.JSONObjectWithData(req, options: NSJSONReadingOptions.AllowFragments, error: &error) as? [[String: AnyObject]] {
return JSON
}
}
return nil
}
That's it!
We can now call the function and extract values from our [[String: AnyObject]] (again, wow) like this:
if let data: [[String: AnyObject]] = getData() {
println(data[0]["PlayerName"]!) // Peyton Manning
}
Update your code with this:
func getRemoteJsonFile() -> [NSDictionary] {
// Create a new URL
let remoteUrl:NSURL? = NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")
let urlString:String = "\(remoteUrl)"
// Check if it's nil
if let actualRemoteUrl = remoteUrl {
// Try to get the data
let fileData:NSData? = NSData(contentsOfURL: actualRemoteUrl)
// Check if it's nil
if let actualFileData = fileData {
// Parse out the dictionaries
let arrayOfDictionaries:[NSDictionary]? = NSJSONSerialization.JSONObjectWithData(actualFileData, options: NSJSONReadingOptions.MutableContainers, error: nil) as [NSDictionary]?
if let actualArrayOfDictionaries = arrayOfDictionaries {
// Successfully parsed out array of dictionaries
return actualArrayOfDictionaries
}
}
}
return [NSDictionary]()
}
This is working fine for me.
For backend communication, my app requires a method to create a certainly structured JSON, and thats where i struggle.
The created JSON is supposed to look like this:
{
"data": {
"color":"yellow",
"size":"big"
}
}
Serializing a Dictionary with the required Data does not really seem to have the option to format the content properly, my best results look like this:
Optional({
Kategorie = Strassenschaeden;
PLZ = 46282;
Strasse = Erftweg;
Unterkategorie = Schlagloch;
})
I didnt find any helpful weblinks for my problem, and since im new to Swift and its documentation Im kinda stuck at the moment.
So my questions are:
Whats the preferred data structure for my JSON data (Dictionary/Array) and how do I create a JSON that is well-formated?
Thanks in advance :)
Edit: This is the interesting part of what i have used to achieve my "best result":
var data: [String: String] = ["Kategorie": "\(Kategorie)", "Unterkategorie": "\(Unterkategorie)", "Strasse": "\(Strasse)","PLZ": "\(PLZ)"]
self.post(data, url: "http://*************") { (succeeded: Bool, msg: String) -> () in
var alert = UIAlertView(title: "Success!", message: msg, delegate: nil, cancelButtonTitle: "Okay.")
func post(params : Dictionary<String, String>, url : String, postCompleted : (succeeded: Bool, msg: String) -> ()) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
let JSONData:NSData = NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions.PrettyPrinted, error: &err)!
var json = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &err) as? NSDictionary
println(json)
Here
let JSONData:NSData = NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions.PrettyPrinted, error: &err)!
var json = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &err) as? NSDictionary
you are converting the params dictionary to JSON data – and then you convert the
JSON data back do a dictionary! What you probably want is to create a string
from the JSON data:
let jsonData = NSJSONSerialization.dataWithJSONObject(params, options: .PrettyPrinted, error: &err)!
let json = NSString(data: jsonData, encoding: NSUTF8StringEncoding)!
println(json)
Remarks:
Properties and variables should have names starting with lower case letters, e.g.
jsonData.
The explicit type annotation :NSData is not needed here, the Swift compiler can
infer the type automatically.
The option can be given as .PrettyPrinted instead of NSJSONWritingOptions.PrettyPrinted, the compiler infers the enumeration type
automatically.
Instead of forced unwrapping with ! you should use optional binding to check
for success.
Just an itch, that no one here recommend swiftyJSON for working with JSON in Swift.
Try it, you will lose your pain of dealing with JSON.
https://github.com/SwiftyJSON/SwiftyJSON
To Read JSON
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let jsonObject = JSON(data: jsonData!)
To Write JSON
let jsonString = jsonObject.rawString()
I have trouble with the NSJSONSeralization.dataWithJSONObject.
This will make my application crash:
#IBAction func sendMessage(sender: AnyObject) {
cachedMessage = messageField.text
messageField.text = ""
let messageData = NSJSONSerialization.dataWithJSONObject(cachedMessage, options: NSJSONWritingOptions.PrettyPrinted, error: nil)
...
}
I hope you can somehow help me...
It will give you an error like : Invalid top-level type in JSON write
If you are trying to create a JSON object then it should be either from Array or Dictionary. Because An object that you want convert in to JSON Must be a Top Level object.
The top level object is an NSArray or NSDictionary.
Try in this way :
var demoArray : NSArray! // (Use it if you want to send data as an Array)
var demoDic : NSDictionary! // (Use it if you want to send data as an Dictionary (Key - Value Pair))
var cachedMessage : String!
cachedMessage = "Sample" // Here your String From textfield
demoArray = [cachedMessage] // Array with your string object
demoDic = ["Your Key":cachedMessage] // Dic with your string object.
You can provide your desired key instead of Your Key
This is how you can create Data from array and Dictionary.
let msgDataOfArray = NSJSONSerialization.dataWithJSONObject(demoArray, options: NSJSONWritingOptions.PrettyPrinted, error:nil)
let msgDataOfDic = NSJSONSerialization.dataWithJSONObject(demoDic, options: NSJSONWritingOptions.PrettyPrinted, error: nil)
If you want to see how your data will look after JSONSerialization process then you can see like below way
var DataToStringForArray = NSString(data: msgDataOfArray!, encoding: NSUTF8StringEncoding)
var DataToStringForDic = NSString(data: msgDataOfDic!, encoding: NSUTF8StringEncoding)
println("Data To String OF Array : \(DataToStringForArray)")
println("Data To String OF Dic : \(DataToStringForDic)")