Wie serialisiere oder konvertiere ich Swift-Objekte in JSON?

81

Dies unter Klasse

class User: NSManagedObject {
  @NSManaged var id: Int
  @NSManaged var name: String
}

Muss konvertiert werden zu

{
    "id" : 98,
    "name" : "Jon Doe"
}

Ich habe versucht, das Objekt manuell an eine Funktion zu übergeben, die die Variablen in ein Wörterbuch setzt und das Wörterbuch zurückgibt. Aber ich würde einen besseren Weg wollen, um dies zu erreichen.

Penkey Suresh
quelle
1
Am besten durchlaufen Sie es, speichern es in Arrays und Dicts und konvertieren es dann .
Schema
Siehe dies - github.com/dankogai/swift-json
Uttam Sinha
@Schemetrical könntest du mir ein Beispiel geben? Ich weiß nicht, wie ich durch ein Objekt laufen soll.
Penkey Suresh
Nun, Sie müssen das Objekt selbst durchgehen, alle Werte übernehmen, es manuell einem Diktat hinzufügen und wiederholen.
Schema
@Schemetrical Ich habe das tatsächlich versucht. Bei großen Objekten nimmt die Kompilierungszeit jedoch dramatisch zu.
Penkey Suresh

Antworten:

126

In Swift 4 können Sie von der erben Codable Typ .

struct Dog: Codable {
    var name: String
    var owner: String
}

// Encode
let dog = Dog(name: "Rex", owner: "Etgar")

let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(dog)
let json = String(data: jsonData, encoding: String.Encoding.utf16)

// Decode
let jsonDecoder = JSONDecoder()
let secondDog = try jsonDecoder.decode(Dog.self, from: jsonData)
Etgar
quelle
21
Der Codierungstyp sollte .utf8 anstelle von .utf16 sein
Chan Jing Hong
2
@ChanJingHong es hängt davon ab, was Sie zu codieren versuchen
Etgar
Funktioniert .utf16 für dieses spezielle Beispiel? Ich habe es versucht, aber es funktioniert nicht.
Chan Jing Hong
@ChanJingHong Seltsam, ich habe es vor 3 Monaten versucht und es hat funktioniert. Ich denke, ich hätte den Codierungstyp auch als Argument in die Decodierungsmethode einfügen sollen. Ich werde das überprüfen.
Etgar
2
Frage war speziell über die Codierung class(was ich brauche) nicht struct.
Gondo
37

Zusammen mit Swift 4 (Foundation) wird es jetzt auf beide Arten nativ unterstützt: JSON-Zeichenfolge für ein Objekt - ein Objekt für JSON-Zeichenfolge. Die Dokumentation von Apple finden Sie hier JSONDecoder () und hier JSONEncoder ()

JSON-String zum Objekt

let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let myStruct = try! decoder.decode(myStruct.self, from: jsonData)

Schnelles Objekt für JSONString

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try! encoder.encode(myStruct)
print(String(data: data, encoding: .utf8)!)

Alle Details und Beispiele finden Sie hier Ultimate Guide to JSON Parsing With Swift 4

Mohacs
quelle
25

UPDATE: Das Codable in Swift 4 eingeführte Protokoll sollte für die meisten JSONParsing-Fälle ausreichend sein . Die folgende Antwort richtet sich an Personen, die in früheren Versionen von Swift und aus früheren Gründen nicht weiterkommen

EVReflection :

  • Dies funktioniert nach dem Prinzip der Reflexion. Dieser Vorgang dauert weniger Code und auch unterstützt NSDictionary, NSCoding, Printable, HashableundEquatable

Beispiel:

    class User: EVObject { # extend EVObject method for the class
       var id: Int = 0
       var name: String = ""
       var friends: [User]? = []
    }

    # use like below
    let json:String = "{\"id\": 24, \"name\": \"Bob Jefferson\", \"friends\": [{\"id\": 29, \"name\": \"Jen Jackson\"}]}"
    let user = User(json: json)

ObjectMapper :

  • Eine andere Möglichkeit ist die Verwendung von ObjectMapper. Dies gibt mehr Kontrolle, erfordert aber auch viel mehr Code.

Beispiel:

    class User: Mappable { # extend Mappable method for the class
       var id: Int?
       var name: String?

       required init?(_ map: Map) {

       }

       func mapping(map: Map) { # write mapping code
          name    <- map["name"]
          id      <- map["id"]
       }

    }

    # use like below
    let json:String = "{\"id\": 24, \"name\": \"Bob Jefferson\", \"friends\": [{\"id\": 29, \"name\": \"Jen Jackson\"}]}"
    let user = Mapper<User>().map(json)
Penkey Suresh
quelle
Unterstützt es auch das Zuordnen von Bildern?
Charlie
1
@ Charlie Entschuldigung, ich bin mir nicht sicher, ob das möglich ist.
Penkey Suresh
1
@ Suresh: Es ist möglich, eine benutzerdefinierte Transformation wie in den gezeigten Beispielen zu schreiben. Ich habe das Bild in String konvertiert und dann im Json-Objekt hinzugefügt. Es hilft sehr speziell bei der Arbeit mit Watch OS
Charlie
1
Hallo, wissen Sie, wie Sie eine Mappable-Klasse initialisieren und Eigenschaften manuell festlegen und dann das Objekt in jsonString konvertieren können?
VAAA
Welche Auswirkungen hat das codierbare Protokoll von Swift 4 / iOS 11? . Können wir damit NSManagedObject in Swift 4 in JSON konvertieren?
user1828845
13

Ich habe ein bisschen an einer kleineren Lösung gearbeitet, die keine Vererbung erfordert. Aber es wurde nicht viel getestet. Es ist ziemlich hässlich atm.

https://github.com/peheje/JsonSerializerSwift

Sie können es auf einen Spielplatz geben, um es zu testen. ZB folgende Klassenstruktur:

//Test nonsense data
class Nutrient {
    var name = "VitaminD"
    var amountUg = 4.2

    var intArray = [1, 5, 9]
    var stringArray = ["nutrients", "are", "important"]
}

class Fruit {
    var name: String = "Apple"
    var color: String? = nil
    var weight: Double = 2.1
    var diameter: Float = 4.3
    var radius: Double? = nil
    var isDelicious: Bool = true
    var isRound: Bool? = nil
    var nullString: String? = nil
    var date = NSDate()

    var optionalIntArray: Array<Int?> = [1, 5, 3, 4, nil, 6]
    var doubleArray: Array<Double?> = [nil, 2.2, 3.3, 4.4]
    var stringArray: Array<String> = ["one", "two", "three", "four"]
    var optionalArray: Array<Int> = [2, 4, 1]

    var nutrient = Nutrient()
}

var fruit = Fruit()
var json = JSONSerializer.toJson(fruit)

print(json)

druckt

{"name": "Apple", "color": null, "weight": 2.1, "diameter": 4.3, "radius": null, "isDelicious": true, "isRound": null, "nullString": null, "date": "2015-06-19 22:39:20 +0000", "optionalIntArray": [1, 5, 3, 4, null, 6], "doubleArray": [null, 2.2, 3.3, 4.4], "stringArray": ["one", "two", "three", "four"], "optionalArray": [2, 4, 1], "nutrient": {"name": "VitaminD", "amountUg": 4.2, "intArray": [1, 5, 9], "stringArray": ["nutrients", "are", "important"]}}
Peheje
quelle
Können Sie bitte die schnelle Version 2.3 bereitstellen?
H Raval
8

Dies ist keine perfekte / automatische Lösung, aber ich glaube, dies ist der idiomatische und native Weg, dies zu tun. Auf diese Weise benötigen Sie keine Bibliotheken oder ähnliches.

Erstellen Sie ein Protokoll wie:

/// A generic protocol for creating objects which can be converted to JSON
protocol JSONSerializable {
    private var dict: [String: Any] { get }
}

extension JSONSerializable {
    /// Converts a JSONSerializable conforming class to a JSON object.
    func json() rethrows -> Data {
        try JSONSerialization.data(withJSONObject: self.dict, options: nil)
    }
}

Implementieren Sie es dann in Ihrer Klasse wie:

class User: JSONSerializable {
    var id: Int
    var name: String

    var dict { return ["id": self.id, "name": self.name]  }
}

Jetzt:

let user = User(...)
let json = user.json()

Hinweis: Wenn Sie jsoneine Zeichenfolge verwenden möchten , können Sie diese ganz einfach in eine Zeichenfolge konvertieren:String(data: json, encoding .utf8)

Downgoat
quelle
6

Einige der obigen Antworten sind völlig in Ordnung, aber ich habe hier eine Erweiterung hinzugefügt, um sie besser lesbar und benutzerfreundlicher zu machen.

extension Encodable {
    var convertToString: String? {
        let jsonEncoder = JSONEncoder()
        jsonEncoder.outputFormatting = .prettyPrinted
        do {
            let jsonData = try jsonEncoder.encode(self)
            return String(data: jsonData, encoding: .utf8)
        } catch {
            return nil
        }
    }
}

struct User: Codable {
     var id: Int
     var name: String
}

let user = User(id: 1, name: "name")
print(user.convertToString!)

// Dies wird wie folgt gedruckt:

{
  "id" : 1,
  "name" : "name"
}
dheeru
quelle
2

Ich bin mir nicht sicher, ob lib / framework existiert, aber ob du es automatisch machen möchtest und Handarbeit vermeiden möchtest :-) bleib bei MirrorType...

class U {

  var id: Int
  var name: String

  init(id: Int, name: String) {
    self.id = id
    self.name = name
  }

}

extension U {

  func JSONDictionary() -> Dictionary<String, Any> {
    var dict = Dictionary<String, Any>()

    let mirror = reflect(self)

    var i: Int
    for i = 0 ; i < mirror.count ; i++ {
      let (childName, childMirror) = mirror[i]

      // Just an example how to check type
      if childMirror.valueType is String.Type {
        dict[childName] = childMirror.value
      } else if childMirror.valueType is Int.Type {
        // Convert to NSNumber for example
        dict[childName] = childMirror.value
      }
    }

    return dict
  }

}

Nehmen Sie es als grobes Beispiel, es fehlt die richtige Konvertierungsunterstützung, es fehlt die Rekursion, ... Es ist einfach MirrorType Demonstration ...

PS Hier ist es erledigt U, aber Sie werden es verbessern NSManagedObjectund dann können Sie alle NSManagedObjectUnterklassen konvertieren . Dies muss nicht in allen Unterklassen / verwalteten Objekten implementiert werden.

zrzka
quelle
Hallo, ich versuche diese Methode, aber jedes Mal bekomme ich 0 als Zählung. Könnten Sie genauer sagen, was ich tun soll? Ich denke, dass @NSManaged Probleme verursacht. Wie verbessere ich NSManagedObject?
Penkey Suresh
Ich habe es nicht versucht @NSManaged. Vielleicht verursacht es Ihr Problem. In diesem Fall würde ich es in Objective-C schreiben und es dann von Swift verwenden.
zrzka
0

2020 | SWIFT 5.1:

(funktioniert auch mit SWIFT 4)


Arbeitsfertige Lösung!

Verwendung:

var msgTemplates = [msgTemlate]()

// load from file
msgTemplates = try! Serializer.load(from: url)!

// save to file
Serializer.save(data: msgTemplates, to: url)

Der folgende Code löst 3 Dinge:

  • 1 Zeichenfolge zum Speichern der Datei
  • 1 Zeichenfolge zum Laden der Datei
  • Fähigkeit, JSON eines codierbaren Elements mit abzurufen / zu drucken element.toJsonString
    import Foundation

    public class Serializer{
        static func save<T>(data: T, to url: URL) where T : Encodable{
            guard let json = data.toJsonString else { return }

            do {
                try json.write(to: url, atomically: true, encoding: String.Encoding.utf8)
            }
            catch { /* error handling here */ }
        }

        static func load<T>(from url: URL) throws -> T? where T : Decodable {
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601 // for human-read date format

            guard let dataStr = try? String(contentsOf: url, encoding: String.Encoding.utf8 ),
                  let data = dataStr.data(using: String.Encoding.utf8 ),
                  let result = try? decoder.decode( T.self , from: data)
            else { return nil }

            return result
        }
    }

    extension Encodable {
        var toJsonString: String? {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted  // nice formatted for reading by human
            encoder.dateEncodingStrategy = .iso8601    // for human-read date format

            do {
                let jsonData = try encoder.encode(self)
                return String(data: jsonData, encoding: .utf8)
            } catch {
                return nil
            }
        }
    }

PS: und ofc Daten müssen codierbar sein:

struct msgTemlate: Codable { 
     //some params
}

PS2: Wenn msgTemlate Enums hat, müssen sie auch codierbar sein

Andrew
quelle
0
struct User:Codable{
 var id:String?
 var name:String?
 init(_ id:String,_ name:String){
   self.id  = id
   self.name = name
 }
}

Jetzt machen Sie einfach Ihr Objekt so

let user = User ("1", "pawan")

do{
      let userJson =  try JSONEncoder().encode(parentMessage) 
            
    }catch{
         fatalError("Unable To Convert in Json")      
    }

Konvertieren Sie dann von json zu Object

let jsonDecoder = JSONDecoder()
do{
   let convertedUser = try jsonDecoder.decode(User.self, from: userJson.data(using: .utf8)!)
 }catch{
   
 }
Pawan Kumar Sharma
quelle