web-dev-qa-db-fra.com

iOS - Mappez un tableau JSON racine avec ObjectMapper dans swift

J'utilise la bibliothèque ObjectMapper pour mapper json avec mes objets mais j'ai quelques problèmes pour mapper un tableau json racine.

C'est le json reçu:

[
   {
       CustomerId = "A000015",
       ...
   },
   {
       CustomerId = "A000016",
       ...
   },
   {
       CustomerId = "A000017",
       ...
   }
]

C'est mon objet

class Customer : Mappable
{
    var CustomerId : String? = nil

    class func newInstance(map: Map) -> Mappable? {
        return Customer()
    }

    func mapping(map: Map) {
        CustomerId   <- map["CustomerId"]
    }
}

Je mappe le json dans mon contrôleur avec

let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSArray

if (error != nil) {
    return completionHandler(nil, error)
} else {
    var customers = Mapper<Customer>().map(json)
}

Mais ça ne marche pas, j'ai essayé Mapper<[Customer]>().map(json) mais ça ne marche pas aussi. Enfin, j'ai essayé de créer un nouvel objet Swift CustomerList contenant un tableau Customer mais cela ne fonctionne pas.

Avez-vous une idée de la façon de mapper json d'un tableau racine?

Merci.

35
Nicolas HENAUX

Je résous enfin mon problème:

La méthode de mappage dans le contrôleur doit être

let json : AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error)

if (error != nil) {
    return completionHandler(nil, error)
} else {
    var customer = Mapper<Customer>().mapArray(json)! //Swift 2
    var customer = Mapper<Customer>().mapArray(JSONArray: json)! //Swift 3
}

Si cela peut aider quelqu'un.

56
Nicolas HENAUX

Utilisation de JSONObjectWithData(::) avec le type de conversion descendante conditionnelle correct

Votre JSON est de type [[String: AnyObject]]. Par conséquent, avec Swift 2, vous pouvez utiliser JSONObjectWithData(::) avec une conversion descendante conditionnelle de type [[String: AnyObject]] Afin d'éviter d'utiliser NSArray ou AnyObject!:

do {
    if let jsonArray = try NSJSONSerialization
        .JSONObjectWithData(data, options: []) as? [[String: AnyObject]] {
        /* perform your ObjectMapper's mapping operation here */
    } else {
        /* ... */
    }
}
catch let error as NSError {
    print(error)
}

Mappage vers Customer à l'aide de la méthode mapArray(:)

La classe ObjectMapper de Mapper fournit une méthode appelée mapArray(:) qui a la déclaration suivante:

public func mapArray(JSONArray: [[String : AnyObject]]) -> [N]?

La documentation ObjectMapper indique à ce sujet:

Mappe un tableau de dictionnaire JSON à un tableau d'objets mappables

Ainsi, votre code final devrait ressembler à ceci:

do {
    if let jsonArray = try NSJSONSerialization
        .JSONObjectWithData(data, options: []) as? [[String: AnyObject]] {
        let customerArray = Mapper<Customer>().mapArray(jsonArray)
        print(customerArray) // customerArray is of type [Customer]?
    } else {
        /* ... */
    }
}
catch let error as NSError {
    print(error)
}

Mappage vers Customer à l'aide de la méthode map(:)

La classe ObjectMapper de Mapper fournit une méthode appelée map(:) qui a la déclaration suivante:

func map(JSONDictionary: [String : AnyObject]) -> N?

La documentation ObjectMapper indique à ce sujet:

Mappe un dictionnaire JSON à un objet conforme à Mappable

Comme alternative au code précédent, le code suivant montre comment mapper votre JSON à Customer à l'aide de map(:):

do {
    if let jsonArray = try NSJSONSerialization
        .JSONObjectWithData(data, options: []) as? [[String: AnyObject]] {
        for element in jsonArray {
            let customer = Mapper<Customer>().map(element)
            print(customer) // customer is of type Customer?
        }
    } else {
        /* ... */
    }
}
catch let error as NSError {
    print(error)
}
9
Imanou Petit

La solution la plus simple est fournie par AlamofireObjectMapper . Utilisez la méthode de commodité responseArray():

Alamofire.request(endpoint).responseArray { (response: DataResponse<[MyMappableClass]>) in

            if let result = response.result.value {
                // Customer array is here
            } else if let error = response.result.error {
                // Handle error
            } else {
                // Handle some other not networking error
            }
        }
3
Bence Pattogato

Dans la même situation dans mon récent Swift 3, capable de résoudre pour obtenir le mappeur d'objets présent dans Array en tant que root.

Convertissez d'abord la chaîne json en objet à l'aide de la sérialisation.

let parsedMapperString = Mapper<Customer>.parseJSONString(JSONString: result) //result is string from json serializer

Ensuite, vous pouvez obtenir le DTO client du dictionnaire MapSet of JSON vers un tableau d'objets mappables.

let customerDto = Mapper<Customer>().mapSet(JSONArray: jsonParsed as! [[String : Any]])

J'espère que ça aide. Merci à @Nicolas qui m'a poussé près pour trouver une solution.

2
byJeevan

Convertissez le tableau en json et vice-versa:

let json = shops.toJSONString()
let shops = Array<Shop>(JSONString: json)
1
comm1x

Un bon moyen de résoudre le problème de mappage d'un tableau racine avec un objet générique consiste à créer un objet générique qui crée une liste avec l'objet à l'intérieur de l'implémentation de classe. Voyons un exemple de ce type d'implémentation ci-dessous:

    Alamofire.request(REQ_URL_STRING, 
       method: REQ_METHOD(eg.: .GET), 
       parameters: REQ_PARAMS, 
       encoding: REQ_ENCODING, 
       headers: REQ_HEADERS).responseObject { (response: DataResponse<GenericResponseList<SingleElement>>) in
            //your code after serialization here
    }

Dans le code ci-dessus, vous remplissez les variables majuscules avec vos propres valeurs. Vérifiez que le retour de réponse dans la fermeture est un objet générique DataResponse d'Alamofire, et j'en ai créé un autre appelé GenericResponseList. Je mets à l'intérieur du "<>" le type de l'objet que je vais obtenir une liste du serveur. Dans mon cas, c'était une liste de SingleElements.

Maintenant, jetez un œil à l'implémentation de GenericResponseList ci-dessous:

final class GenericResponseList<T: Mappable>: Mappable {

    var result: [T]?

    required convenience init?(map: Map) {
        self.init()
    }

    func mapping(map: Map) {
        result <- map["result"]
    }
}

Jetez un oeil, j'ai une variable à l'intérieur de la classe qui est une liste du type générique que j'ai envoyé à cette classe.

var result: [T]?

Alors maintenant, lorsque vous obtenez votre JSON, il le convertira en une liste de SingleElement.

J'espère que cela a aidé :)

1
Alcivanio