简体   繁体   中英

How can I Read/Write array of objects from/to JSON using a Swift class that matches JSON from NewtonSoft (JSON.Net) component?

I'm trying to read a JSON file that I've created using the NewtonSoft JSON.Net parser in another (Windows) program. The JSON was created by the JSON.Net component when it serialized an array of objects. The sample JSON looks like the following (for this example I'm just showing two of the objects):

[{"MaxLength":23,"HasSpecialChars":false,"HasUpperCase":true,"Key":"firstOne"},
{"MaxLength":0,"HasSpecialChars":false,"HasUpperCase":false,"Key":"secondOne"}]

Notice that this is an array of objects in json.

Now I need some Swift code that will read this JSON in and write it out after values are altered in the program.

What I've Tried

I found this SO entry : Reading in a JSON File Using Swift However, to get an array of objects, that entry uses separate structs that are Codable like the following:

struct ResponseData: Decodable {
    var thisNameShowsUpInJson: [SiteKey]
}

That forces the outer array to have it's own name property in the json.

For example, the only way the code at that post works is if my JSON is altered to include an outer object with a name (SiteKey) like the following:

 {"thisNameShowsUpInJson": [{"MaxLength":23,"HasSpecialChars":false,"HasUpperCase":true,"Key":"firstOne"},
   {"MaxLength":0,"HasSpecialChars":false,"HasUpperCase":false,"Key":"secondOne"}]
}

However, that is not correct for the way that JSON.Net writes an array of objects to a file.

Here's my simple Swift class that I want to serialize and deserialize:

class SiteKey : Codable{
    var Key : String
    var MaxLength : Int
    var HasSpecialChars : Bool
    var HasUpperCase : Bool

    init(key : String, maxLength : Int,
         hasSpecialChars : Bool,
         hasUpperCase : Bool){
        Key = key;
        MaxLength = maxLength;
        HasSpecialChars = hasSpecialChars;
        HasUpperCase = hasUpperCase;
    } 
}

I'd like to read the data from a named file and deserialize the data into objects. Then, I'd like to serialize the in memory objects back out to a file like my example.

Imagine that you have an array of codable objects

var array = [SiteKey]()

then you can simply encode the entire array to Data using JSONEncoder

do {
    let encoded = try JSONEncoder().encode(array)
} catch { print(error) }

To decode Data to your array of objects you can use JSONDecoder

do {
    array = try JSONDecoder().decode([SiteKey].self, from: encoded)
} catch { print(error) }

My suggestions:

  • make your class struct instead, then you can remove hard-coded init since you get one for free
  • name variables with small capital letter and then use coding keys for renaming it while encoding/decoding

struct SiteKey : Codable {

    var key : String
    var maxLength : Int
    var hasSpecialChars : Bool
    var hasUpperCase : Bool

    enum CodingKeys: String, CodingKey {
        case key = "Key"
        case maxLength = "MaxLength"
        case hasSpecialChars = "HasSpecialChars"
        case hasUpperCase = "HasUpperCase"
    }

}

I discovered the code I need to use in Swift which allows me to read and write the JSON (array of objects) that is output by JSON.Net.

I've added two methods to my SiteKey object :

func loadJson(filename fileName: String) -> [SiteKey]
func writeJson(filename fileName: String, allSiteKeys : [SiteKey])

The first function takes a string that points to a json file and returns the array of SiteKeys that is in the file. The second function takes a filename and the array of SiteKey objects and writes them to the file.

Here's the altered SiteKey class with the added functions.

class SiteKey : Codable{
    var Key : String
    var MaxLength : Int
    var HasSpecialChars : Bool
    var HasUpperCase : Bool

    init(key : String, maxLength : Int,
         hasSpecialChars : Bool,
         hasUpperCase : Bool){
        Key = key;
        MaxLength = maxLength;
        HasSpecialChars = hasSpecialChars;
        HasUpperCase = hasUpperCase;
    }

    func loadJson(filename fileName: String) -> [SiteKey]? {
        if let url = Bundle.main.url(forAuxiliaryExecutable: fileName) {
            do {
                let data = try Data(contentsOf: url)
                let decoder = JSONDecoder()
                let allKeys = try decoder.decode([SiteKey].self, from: data)
                return allKeys
            } catch {
                print("error:\(error)")
            }
        }
        return nil
    }

    func writeJson(filename fileName: String, allSiteKeys : [SiteKey]){
        let Data = try? JSONEncoder().encode(allSiteKeys)

            let pathAsURL = URL(fileURLWithPath: fileName)
            do {
                try Data?.write(to: pathAsURL)
            }
            catch {
                print("Failed to write JSON data: \(error.localizedDescription)")
            }
    }
}

Here's the usage:

let newSiteKey = siteKey.loadJson(filename: "/Users/fakeUser/Documents/Dev/all.json")

When the loadJson method returns the newSiteKey will contain an array of SiteKey class objects that can be iterated through.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM