How to write a Swift 4 encoded JSON to a file?

10,409

JSONSerialization. Your JSONSerialization.isValidJSONObject usage is wrong. As the documentation states:

A Foundation 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.

As such, Data or String types aren’t valid at all ;)

Writing the encoded data. To actually write the generated Data, use the corresponding Data.write(to: URL) method instead. For instance:

if let encodedData = try? JSONEncoder().encode(obj) {
    let path = "/path/to/obj.json"
    let pathAsURL = URL(fileURLWithPath: path)
    do {
        try encodedData.write(to: pathAsURL)
    } 
    catch {
        print("Failed to write JSON data: \(error.localizedDescription)")
    }
}

As long as the JSON data is generated by the standard JSONEncoder no additional validation is really made necessary ;)

Share:
10,409
BadmintonCat
Author by

BadmintonCat

iOS / Mac OSX / Unity3D developer and former Flash/AS3 developer in Tokyo, Japan. Also delving into C# .NET development.

Updated on June 14, 2022

Comments

  • BadmintonCat
    BadmintonCat about 2 years

    How do you write a JSON object encoded via Swift 4 Codable protocol to a file? Before Swift 4 I used JSONSerialization.writeJSONObject but JSONSerialization.isValidJSONObject now returns false on the created data (or string). An example:

    import Foundation
    
    class Shark : Codable
    {
        var name:String = ""
        var carnivorous:Bool = true
        var numOfTeeth:Int = 0
        var hobbies:[String] = []
    }
    
    class JSON
    {
        class func encode<T:Encodable>(_ obj:T) -> String?
        {
            if let encodedData = try? JSONEncoder().encode(obj)
            {
                return String(data: encodedData, encoding: .utf8)
            }
            return nil
        }
    
        class func writeToStream(data:Any, path:String) -> Bool
        {
            var success = false
            if JSONSerialization.isValidJSONObject(data)
            {
                if let stream = OutputStream(toFileAtPath: "\(path)", append: false)
                {
                    stream.open()
                    var error:NSError?
                    JSONSerialization.writeJSONObject(data, to: stream, options: [], error: &error)
                    stream.close()
                    if let error = error
                    {
                        print("Failed to write JSON data: \(error.localizedDescription)")
                        success = false
                    }
                }
                else
                {
                    print("Could not open JSON file stream at \(path).")
                    success = false
                }
            }
            else
            {
                print("Data is not a valid format for JSON serialization: \(data)")
                success = false
            }
            return success
        }
    }
    
    
    let shark = Shark()
    shark.name = "Nancy"
    shark.carnivorous = true
    shark.numOfTeeth = 48
    shark.hobbies = ["Dancing", "Swiming", "Eating people"]
    
    if let jsonString = JSON.encode(shark)
    {
        let success = JSON.writeToStream(data: jsonString.data(using: .utf8), path: "\(NSHomeDirectory())/Documents")
    }
    

    Both of these formats are invalid for JSONSerialization.isValidJSONObject():

    JSON.writeToStream(data: jsonString, path: "\(NSHomeDirectory())/Documents")
    JSON.writeToStream(data: jsonString.data(using: .utf8), path: "\(NSHomeDirectory())/Documents")
    

    Data is not a valid format for JSON serialization:
    {"numOfTeeth":48,"hobbies":["Dancing","Swiming","Eating people"],"name":"Nancy","carnivorous":true}
    Data is not a valid format for JSON serialization: Optional(99 bytes)

    How do I get it passed for JSON validation and then write it to a file?