web-dev-qa-db-fra.com

Comment sérialiser ou convertir des objets Swift en JSON?

Cette classe ci-dessous 

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

Doit être converti en 

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

J'ai essayé de passer manuellement l'objet à une fonction qui définit les variables dans un dictionnaire et renvoie le dictionnaire. Mais je voudrais un meilleur moyen d'accomplir cela.

51
Penkey Suresh

Dans Swift 4, vous pouvez hériter du type Codable.

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 dog = try jsonDecoder.decode(Dog.self, from: jsonData)
60
Etgar

UPDATE:Codable protocole introduit dans Swift 4 devrait être suffisant pour la plupart des cas d'analyse JSON. La réponse ci-dessous est pour les personnes qui sont bloquées dans les versions précédentes de Swift et pour des raisons d'héritage

EVReflection :

  • Cela fonctionne de principe de réflexion. Cela prend moins de code et prend également en charge NSDictionary, NSCoding, Printable, Hashable et Equatable

Exemple:

    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 :

  • Une autre façon consiste à utiliser ObjectMapper. Cela donne plus de contrôle mais prend également beaucoup plus de code.

Exemple:

    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)
24
Penkey Suresh

Avec Swift 4 (Foundation), il est maintenant pris en charge de manière native dans les deux sens, chaîne JSON en objet - un objet en chaîne JSON. Veuillez consulter la documentation Apple ici JSONDecoder () et ici JSONEncoder ()

Chaîne JSON en objet

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

Objet rapide à JSONString

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

Vous trouverez tous les détails et les exemples ici Guide ultime de l'analyse syntaxique JSON avec Swift 4

22
mohacs

J'ai travaillé un peu sur une solution plus petite qui n'exige pas d'héritage. Mais cela n'a pas été beaucoup testé. C'est assez moche atm.

https://github.com/peheje/JsonSerializerSwift

Vous pouvez le passer dans un terrain de jeu pour le tester. Par exemple. structure de classe suivante:

//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)

empreintes

{"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"]}}
13
Peheje

Ce n'est pas une solution parfaite/automatique, mais je pense que c'est la manière idiomatique et native de le faire. De cette façon, vous n’avez pas besoin de bibliothèques ou autres.

Créez un protocole tel que:

/// 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)
    }
}

Puis implémentez-le dans votre classe, par exemple:

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

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

À présent:

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

Remarque: si vous voulez json en tant que chaîne, il suffit tout simplement de convertir en chaîne: String(data: json, encoding .utf8)

7
Downgoat

Certaines des réponses sont tout à fait correctes, mais j’ai ajouté une extension ici, juste pour la rendre beaucoup plus lisible et utilisable.

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!)

// Ceci est imprimé comme suit:

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

Vous ne savez pas si lib/framework existe, mais si vous souhaitez le faire automatiquement et que vous souhaitez éviter le travail manuel :-) restez avec 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
  }

}

Prenons-le comme un exemple approximatif: manque de prise en charge de la conversion, de récursivité,… Ce n'est qu'une démonstration MirrorType….

P.S. Ici, c'est fait dans U, mais vous allez améliorer NSManagedObject et ensuite vous pourrez convertir toutes les sous-classes NSManagedObject. Pas besoin d'implémenter ceci dans tous les objets de sous-classes/gérés.

2
zrzka
struct User:Codable{
 var id:String?
 var name:String?
 init(_ id:String,_ name:String){
   self.id  = id
   self.name = name
 }
}

Maintenant, faites votre objet comme ceci

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

do{
      let userJson = data: try JSONEncoder().encode(parentMessage), encoding:.utf8)

    }catch{
         fatalError("Unable To Convert in Json")      
    }

Puis reconvertir de json en objet

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

 }
0
Pawan kumar sharma