¿Cómo convertir una cadena JSON a un diccionario?

178

Quiero hacer una función en mi proyecto rápido que convierta String a formato json de diccionario, pero recibí un error:

No se puede convertir el tipo de expresión (@lvalue NSData, opciones: IntegerLitralConvertible ...

Este es mi código:

func convertStringToDictionary (text:String) -> Dictionary<String,String> {

    var data :NSData = text.dataUsingEncoding(NSUTF8StringEncoding)!
    var json :Dictionary = NSJSONSerialization.JSONObjectWithData(data, options:0, error: nil)
    return json
} 

Hago esta función en Objective-C:

- (NSDictionary*)convertStringToDictionary:(NSString*)string {
  NSError* error;
  //giving error as it takes dic, array,etc only. not custom object.
  NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
  id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  return json;
}
trousout
fuente
¿Cuál es el valor y la cantidad que desea en el diccionario?
Amit89
1
Actualizo mi pregunta ... ¡@ Amit89 no importa cuál sea el valor y la clave!
trousout
@ Amit89 Quiero cuando le doy texto a la función regresar formato de diccionario json
trousout
let jsonDictionary = try JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: String];
Vahid Amiri

Respuestas:

384

Advertencia: este es un método conveniente para convertir una cadena JSON en un diccionario si, por alguna razón, tiene que trabajar desde una cadena JSON. Pero si tiene los datos JSON disponibles, debería trabajar con los datos , sin usar una cadena.

Swift 3

func convertToDictionary(text: String) -> [String: Any]? {
    if let data = text.data(using: .utf8) {
        do {
            return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
        } catch {
            print(error.localizedDescription)
        }
    }
    return nil
}

let str = "{\"name\":\"James\"}"

let dict = convertToDictionary(text: str)

Swift 2

func convertStringToDictionary(text: String) -> [String:AnyObject]? {
    if let data = text.dataUsingEncoding(NSUTF8StringEncoding) {
        do {
            return try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String:AnyObject]
        } catch let error as NSError {
            print(error)
        }
    }
    return nil
}

let str = "{\"name\":\"James\"}"

let result = convertStringToDictionary(str)

Respuesta original de Swift 1:

func convertStringToDictionary(text: String) -> [String:String]? {
    if let data = text.dataUsingEncoding(NSUTF8StringEncoding) {
        var error: NSError?
        let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? [String:String]
        if error != nil {
            println(error)
        }
        return json
    }
    return nil
}

let str = "{\"name\":\"James\"}"

let result = convertStringToDictionary(str) // ["name": "James"]

if let name = result?["name"] { // The `?` is here because our `convertStringToDictionary` function returns an Optional
    println(name) // "James"
}

En su versión, no pasó los parámetros adecuados a NSJSONSerialization y olvidó lanzar el resultado. Además, es mejor verificar el posible error. Última nota: esto funciona solo si su valor es una Cadena. Si pudiera ser de otro tipo, sería mejor declarar la conversión del diccionario así:

let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? [String:AnyObject]

y, por supuesto, también necesitaría cambiar el tipo de retorno de la función:

func convertStringToDictionary(text: String) -> [String:AnyObject]? { ... }
Eric Aya
fuente
52

He actualizado la respuesta de Eric D para Swift 5 :

 func convertStringToDictionary(text: String) -> [String:AnyObject]? {
    if let data = text.data(using: .utf8) {
        do {
            let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject]
            return json
        } catch {
            print("Something went wrong")
        }
    }
    return nil
}
Chris Byatt
fuente
Este ejemplo devuelve nil. let x = "{\" a \ ": [\" b \ ": \" B \ ", \" c \ ": \" C \ "]}" let result = convertStringToDictionary (x)
Syed Tariq
23

Swift 3 :

if let data = text.data(using: String.Encoding.utf8) {
    do {
        let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any]
        print(json)
    } catch {
        print("Something went wrong")
    }
}
Danut Pralea
fuente
15

Con Swift 3, JSONSerializationtiene un método llamado json​Object(with:​options:​). json​Object(with:​options:​)tiene la siguiente declaración:

class func jsonObject(with data: Data, options opt: JSONSerialization.ReadingOptions = []) throws -> Any

Devuelve un objeto Foundation a partir de datos JSON dados.

Cuando lo usa json​Object(with:​options:​), tiene que lidiar con el manejo de errores ( try, try?o try!) y la conversión de tipos (desde Any). Por lo tanto, puede resolver su problema con uno de los siguientes patrones.


# 1 Usando un método que arroja y devuelve un tipo no opcional

import Foundation

func convertToDictionary(from text: String) throws -> [String: String] {
    guard let data = text.data(using: .utf8) else { return [:] }
    let anyResult: Any = try JSONSerialization.jsonObject(with: data, options: [])
    return anyResult as? [String: String] ?? [:]
}

Uso:

let string1 = "{\"City\":\"Paris\"}"
do {
    let dictionary = try convertToDictionary(from: string1)
    print(dictionary) // prints: ["City": "Paris"]
} catch {
    print(error)
}
let string2 = "{\"Quantity\":100}"
do {
    let dictionary = try convertToDictionary(from: string2)
    print(dictionary) // prints [:]
} catch {
    print(error)
}
let string3 = "{\"Object\"}"
do {
    let dictionary = try convertToDictionary(from: string3)
    print(dictionary)
} catch {
    print(error) // prints: Error Domain=NSCocoaErrorDomain Code=3840 "No value for key in object around character 9." UserInfo={NSDebugDescription=No value for key in object around character 9.}
}

# 2 Usando un método que arroja y devuelve un tipo opcional

import Foundation

func convertToDictionary(from text: String) throws -> [String: String]? {
    guard let data = text.data(using: .utf8) else { return [:] }
    let anyResult: Any = try JSONSerialization.jsonObject(with: data, options: [])
    return anyResult as? [String: String]
}

Uso:

let string1 = "{\"City\":\"Paris\"}"
do {
    let dictionary = try convertToDictionary(from: string1)
    print(String(describing: dictionary)) // prints: Optional(["City": "Paris"])
} catch {
    print(error)
}
let string2 = "{\"Quantity\":100}"
do {
    let dictionary = try convertToDictionary(from: string2)
    print(String(describing: dictionary)) // prints nil
} catch {
    print(error)
}
let string3 = "{\"Object\"}"
do {
    let dictionary = try convertToDictionary(from: string3)
    print(String(describing: dictionary))
} catch {
    print(error) // prints: Error Domain=NSCocoaErrorDomain Code=3840 "No value for key in object around character 9." UserInfo={NSDebugDescription=No value for key in object around character 9.}
}

# 3 Usar un método que no arroje y devuelva un tipo no opcional

import Foundation

func convertToDictionary(from text: String) -> [String: String] {
    guard let data = text.data(using: .utf8) else { return [:] }
    let anyResult: Any? = try? JSONSerialization.jsonObject(with: data, options: [])
    return anyResult as? [String: String] ?? [:]
}

Uso:

let string1 = "{\"City\":\"Paris\"}"
let dictionary1 = convertToDictionary(from: string1)
print(dictionary1) // prints: ["City": "Paris"]
let string2 = "{\"Quantity\":100}"
let dictionary2 = convertToDictionary(from: string2)
print(dictionary2) // prints: [:]
let string3 = "{\"Object\"}"
let dictionary3 = convertToDictionary(from: string3)
print(dictionary3) // prints: [:]

# 4. Usar un método que no arroje y devuelva un tipo opcional

import Foundation

func convertToDictionary(from text: String) -> [String: String]? {
    guard let data = text.data(using: .utf8) else { return nil }
    let anyResult = try? JSONSerialization.jsonObject(with: data, options: [])
    return anyResult as? [String: String]
}

Uso:

let string1 = "{\"City\":\"Paris\"}"
let dictionary1 = convertToDictionary(from: string1)
print(String(describing: dictionary1)) // prints: Optional(["City": "Paris"])
let string2 = "{\"Quantity\":100}"
let dictionary2 = convertToDictionary(from: string2)
print(String(describing: dictionary2)) // prints: nil
let string3 = "{\"Object\"}"
let dictionary3 = convertToDictionary(from: string3)
print(String(describing: dictionary3)) // prints: nil
Imanou Petit
fuente
8

Swift 4

extension String {
    func convertToDictionary() -> [String: Any]? {
        if let data = self.data(using: .utf8) {
            do {
                return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
            } catch {
                print(error.localizedDescription)
            }
        }
        return nil
    }
}
WM
fuente
2
Solo otra copia pegar
J. Doe
1
si no usa opciones, simplemente no las escriba: JSONSerialization.jsonObject (con: datos)
Zaporozhchenko Oleksandr
6

Swift 5

extension String {
    func convertToDictionary() -> [String: Any]? {
        if let data = data(using: .utf8) {
            return try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
        }
        return nil
    }
}
blancoq
fuente
4

Encontré un código que convierte la cadena json a NSDictionary o NSArray. Solo agrega la extensión.

SWIFT 3.0

CÓMO UTILIZAR

let jsonData = (convertedJsonString as! String).parseJSONString

EXTENSIÓN

extension String
{
var parseJSONString: AnyObject?
{
    let data = self.data(using: String.Encoding.utf8, allowLossyConversion: false)
    if let jsonData = data
    {
        // Will return an object or nil if JSON decoding fails
        do
        {
            let message = try JSONSerialization.jsonObject(with: jsonData, options:.mutableContainers)
            if let jsonResult = message as? NSMutableArray {
                return jsonResult //Will return the json array output
            } else if let jsonResult = message as? NSMutableDictionary {
                return jsonResult //Will return the json dictionary output
            } else {
                return nil
            }
        }
        catch let error as NSError
        {
            print("An error occurred: \(error)")
            return nil
        }
    }
    else
    {
        // Lossless conversion of the string was not possible
        return nil
    }
}

}

Amol Pokale
fuente
No use NSMutable...tipos de colección en Swift en absoluto. Escribir cast to NSMutable...nunca dará como resultado un objeto mutable. Y un tipo no especificado en Swift 3+ es Any, no AnyObject.
vadiano
3

Detalles

  • Xcode versión 10.3 (10G8), Swift 5

Solución

import Foundation

// MARK: - CastingError

struct CastingError: Error {
    let fromType: Any.Type
    let toType: Any.Type
    init<FromType, ToType>(fromType: FromType.Type, toType: ToType.Type) {
        self.fromType = fromType
        self.toType = toType
    }
}

extension CastingError: LocalizedError {
    var localizedDescription: String { return "Can not cast from \(fromType) to \(toType)" }
}

extension CastingError: CustomStringConvertible { var description: String { return localizedDescription } }

// MARK: - Data cast extensions

extension Data {
    func toDictionary(options: JSONSerialization.ReadingOptions = []) throws -> [String: Any] {
        return try to(type: [String: Any].self, options: options)
    }

    func to<T>(type: T.Type, options: JSONSerialization.ReadingOptions = []) throws -> T {
        guard let result = try JSONSerialization.jsonObject(with: self, options: options) as? T else {
            throw CastingError(fromType: type, toType: T.self)
        }
        return result
    }
}

// MARK: - String cast extensions

extension String {
    func asJSON<T>(to type: T.Type, using encoding: String.Encoding = .utf8) throws -> T {
        guard let data = data(using: encoding) else { throw CastingError(fromType: type, toType: T.self) }
        return try data.to(type: T.self)
    }

    func asJSONToDictionary(using encoding: String.Encoding = .utf8) throws -> [String: Any] {
        return try asJSON(to: [String: Any].self, using: encoding)
    }
}

// MARK: - Dictionary cast extensions

extension Dictionary {
    func toData(options: JSONSerialization.WritingOptions = []) throws -> Data {
        return try JSONSerialization.data(withJSONObject: self, options: options)
    }
}

Uso

let value1 = try? data.toDictionary()
let value2 = try? data.to(type: [String: Any].self)
let value3 = try? data.to(type: [String: String].self)
let value4 = try? string.asJSONToDictionary()
let value5 = try? string.asJSON(to: [String: String].self)

Muestra de prueba

No olvide pegar el código de la solución aquí.

func testDescriber(text: String, value: Any) {
    print("\n//////////////////////////////////////////")
    print("-- \(text)\n\n  type: \(type(of: value))\n  value: \(value)")
}

let json1: [String: Any] = ["key1" : 1, "key2": true, "key3" : ["a": 1, "b": 2], "key4": [1,2,3]]
var jsonData = try? json1.toData()
testDescriber(text: "Sample test of func toDictionary()", value: json1)
if let data = jsonData {
    print("  Result: \(String(describing: try? data.toDictionary()))")
}

testDescriber(text: "Sample test of func to<T>() -> [String: Any]", value: json1)
if let data = jsonData {
    print("  Result: \(String(describing: try? data.to(type: [String: Any].self)))")
}

testDescriber(text: "Sample test of func to<T>() -> [String] with cast error", value: json1)
if let data = jsonData {
    do {
        print("  Result: \(String(describing: try data.to(type: [String].self)))")
    } catch {
        print("  ERROR: \(error)")
    }
}

let array = [1,4,5,6]
testDescriber(text: "Sample test of func to<T>() -> [Int]", value: array)
if let data = try? JSONSerialization.data(withJSONObject: array) {
    print("  Result: \(String(describing: try? data.to(type: [Int].self)))")
}

let json2 = ["key1": "a", "key2": "b"]
testDescriber(text: "Sample test of func to<T>() -> [String: String]", value: json2)
if let data = try? JSONSerialization.data(withJSONObject: json2) {
    print("  Result: \(String(describing: try? data.to(type: [String: String].self)))")
}

let jsonString = "{\"key1\": \"a\", \"key2\": \"b\"}"
testDescriber(text: "Sample test of func to<T>() -> [String: String]", value: jsonString)
print("  Result: \(String(describing: try? jsonString.asJSON(to: [String: String].self)))")

testDescriber(text: "Sample test of func to<T>() -> [String: String]", value: jsonString)
print("  Result: \(String(describing: try? jsonString.asJSONToDictionary()))")

let wrongJsonString = "{\"key1\": \"a\", \"key2\":}"
testDescriber(text: "Sample test of func to<T>() -> [String: String] with JSONSerialization error", value: jsonString)
do {
    let json = try wrongJsonString.asJSON(to: [String: String].self)
    print("  Result: \(String(describing: json))")
} catch {
    print("  ERROR: \(error)")
}

Registro de prueba

//////////////////////////////////////////
-- Sample test of func toDictionary()

  type: Dictionary<String, Any>
  value: ["key4": [1, 2, 3], "key2": true, "key3": ["a": 1, "b": 2], "key1": 1]
  Result: Optional(["key4": <__NSArrayI 0x600002a35380>(
1,
2,
3
)
, "key2": 1, "key3": {
    a = 1;
    b = 2;
}, "key1": 1])

//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: Any]

  type: Dictionary<String, Any>
  value: ["key4": [1, 2, 3], "key2": true, "key3": ["a": 1, "b": 2], "key1": 1]
  Result: Optional(["key4": <__NSArrayI 0x600002a254d0>(
1,
2,
3
)
, "key2": 1, "key1": 1, "key3": {
    a = 1;
    b = 2;
}])

//////////////////////////////////////////
-- Sample test of func to<T>() -> [String] with cast error

  type: Dictionary<String, Any>
  value: ["key4": [1, 2, 3], "key2": true, "key3": ["a": 1, "b": 2], "key1": 1]
  ERROR: Can not cast from Array<String> to Array<String>

//////////////////////////////////////////
-- Sample test of func to<T>() -> [Int]

  type: Array<Int>
  value: [1, 4, 5, 6]
  Result: Optional([1, 4, 5, 6])

//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: String]

  type: Dictionary<String, String>
  value: ["key1": "a", "key2": "b"]
  Result: Optional(["key1": "a", "key2": "b"])

//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: String]

  type: String
  value: {"key1": "a", "key2": "b"}
  Result: Optional(["key1": "a", "key2": "b"])

//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: String]

  type: String
  value: {"key1": "a", "key2": "b"}
  Result: Optional(["key1": a, "key2": b])

//////////////////////////////////////////
-- Sample test of func to<T>() -> [String: String] with JSONSerialization error

  type: String
  value: {"key1": "a", "key2": "b"}
  ERROR: Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 21." UserInfo={NSDebugDescription=Invalid value around character 21.}
Vasily Bodnarchuk
fuente
0

para swift 5, escribo una demostración para verificarlo.

extension String {

    /// convert JsonString to Dictionary
    func convertJsonStringToDictionary() -> [String: Any]? {
        if let data = data(using: .utf8) {
            return (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any]
        }

        return nil
    }
}

let str = "{\"name\":\"zgpeace\"}"
let dict = str.convertJsonStringToDictionary()
print("string > \(str)")  
// string > {"name":"zgpeace"}
print("dicionary > \(String(describing: dict))") 
// dicionary > Optional(["name": zgpeace])
Zgpeace
fuente
-1
let JSONData = jsonString.data(using: .utf8)!

let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)

guard let userDictionary = jsonResult as? Dictionary<String, AnyObject> else {
            throw NSError()}
t.ios
fuente