47

I created a struct and want to save it as a JSON-file.

struct Sentence {
    var sentence = ""
    var lang = ""
}

var s = Sentence()
s.sentence = "Hello world"
s.lang = "en"
print(s)

...which results in:

Sentence(sentence: "Hello world", lang: "en")

But how can I convert the struct object to something like:

{
    "sentence": "Hello world",
    "lang": "en"
}
ixany
  • 5,433
  • 9
  • 41
  • 65
  • 1
    Possible duplicate of [Convert Dictionary to JSON in Swift](http://stackoverflow.com/questions/29625133/convert-dictionary-to-json-in-swift) (as a hint, you can only convert an array or dictionary to JSON, so basically you need a dictionary representation of the value you want to put in JSON format). – nhgrif Oct 17 '15 at 11:30
  • Essentially, it would be great if I could convert the struct to a json-like String. When only arrays and dictionaries are convertable to JSON, my question is: How to convert a struct to a dictionary? – ixany Oct 17 '15 at 11:34
  • There are 3rd party libraries such as https://github.com/Hearst-DD/ObjectMapper. I have no experience with that, however. – Martin R Oct 17 '15 at 11:37

4 Answers4

86

Swift 4 introduces the Codable protocol which provides a very convenient way to encode and decode custom structs.

struct Sentence : Codable {
    let sentence : String
    let lang : String
}

let sentences = [Sentence(sentence: "Hello world", lang: "en"), 
                 Sentence(sentence: "Hallo Welt", lang: "de")]

do {
    let jsonData = try JSONEncoder().encode(sentences)
    let jsonString = String(data: jsonData, encoding: .utf8)!
    print(jsonString) // [{"sentence":"Hello world","lang":"en"},{"sentence":"Hallo Welt","lang":"de"}]
    
    // and decode it back
    let decodedSentences = try JSONDecoder().decode([Sentence].self, from: jsonData)
    print(decodedSentences)
} catch { print(error) }
pkamb
  • 33,281
  • 23
  • 160
  • 191
vadian
  • 274,689
  • 30
  • 353
  • 361
17

Swift 4 supports the Encodable protocol e.g.

struct Sentence: Encodable {
    var sentence: String?
    var lang: String?
}

let sentence = Sentence(sentence: "Hello world", lang: "en")

Now you can automatically convert your Struct into JSON using a JSONEncoder:

let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(sentence)

Print it out:

let jsonString = String(data: jsonData, encoding: .utf8)
print(jsonString)

{
    "sentence": "Hello world",
    "lang": "en"
}

https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types

Brett
  • 2,635
  • 22
  • 35
12

Use the NSJSONSerialization class.

Using this for reference, you may need to create a function which returns the JSON serialized string. In this function you could take the required properties and create a NSDictionary from them and use the class mentioned above.

Something like this:

struct Sentence {
    var sentence = ""
    var lang = ""

    func toJSON() -> String? {
        let props = ["Sentence": self.sentence, "lang": lang]
        do {
            let jsonData = try NSJSONSerialization.dataWithJSONObject(props,
            options: .PrettyPrinted)
            return String(data: jsonData, encoding: NSUTF8StringEncoding)
        } catch let error {
            print("error converting to json: \(error)")
            return nil
        }
    }

}

Because your struct only has two properties it might be easier to just build the JSON string yourself.

Scriptable
  • 19,402
  • 5
  • 56
  • 72
  • The return of `toJSON()` should be `NSData`. Also, the second key in the `props` dictionary you've created is wrong. – nhgrif Oct 17 '15 at 11:41
  • I've updated the second key to be a string, thanks. The second point depends on what the user wants returning – Scriptable Oct 17 '15 at 11:42
11

Here's a nice extension and a method for JSON encoding/decoding:

extension Encodable {
    
    func toJSONString() -> String {
        let jsonData = try! JSONEncoder().encode(self)
        return String(data: jsonData, encoding: .utf8)!
    }
    
}

func instantiate<T: Decodable>(jsonString: String) -> T? {
    return try? JSONDecoder().decode(T.self, from: jsonString.data(using: .utf8)!)
}

Sample usage:

struct Sentence: Codable {
    var sentence = ""
    var lang = ""
}

let sentence = Sentence(sentence: "Hello world", lang: "en")
let jsonStr = sentence.toJSONString()
print(jsonStr)      // prints {"lang":"en","sentence":"Hello world"}

let sentenceFromJSON: Sentence? = instantiate(jsonString: jsonStr)
print(sentenceFromJSON!)    // same as original sentence
algrid
  • 5,600
  • 3
  • 34
  • 37