¿Hay alguna forma de imprimir diccionarios Swift en la consola?

92
NSDictionary *dictionary = @{@"A" : @"alfa",
                             @"B" : @"bravo",
                             @"C" : @"charlie",
                             @"D" : @"delta",
                             @"E" : @"echo",
                             @"F" : @"foxtrot"};
NSLog(@"%@", dictionary.description);

imprime lo siguiente en la consola:

{
    A = alfa;
    B = bravo;
    C = charlie;
    D = delta;
    E = echo;
    F = foxtrot;
}

let dictionary: [String : String] = ["A" : "alfa",
                                     "B" : "bravo",
                                     "C" : "charlie",
                                     "D" : "delta",
                                     "E" : "echo",
                                     "F" : "foxtrot"];
print(dictionary)

imprime lo siguiente en la consola:

["B": "bravo", "A": "alfa", "F": "foxtrot", "C": "charlie", "D": "delta", "E": "echo"]

¿Hay alguna forma en Swift de llegar a diccionarios impresos bonitos donde cada par clave-valor ocupa una nueva línea?

Toland Hon
fuente
7
Podría utilizar dump, por ejemplo, si el objetivo es inspeccionar el diccionario. stackoverflow.com/documentation/swift/3966/logging-in-swift/…
Eric Aya
13
print(dictionary as! NSDictionary) ¿truco barato?
BaseZen
Realmente soy la sugerencia de dump () ya que no requiere escribir ningún código o emitirlo. @EricAya, si publicas una respuesta con ese comentario, la marcaré como la respuesta.
Toland Hon
1
@TolandHon Hecho. He respondido con un ejemplo del resultado.
Eric Aya

Respuestas:

99

Podría usar dump , por ejemplo, si el objetivo es inspeccionar el diccionario. dumpes parte de la biblioteca estándar de Swift.

Uso:

let dictionary: [String : String] = ["A" : "alfa",
                                     "B" : "bravo",
                                     "C" : "charlie",
                                     "D" : "delta",
                                     "E" : "echo",
                                     "F" : "foxtrot"]

dump(dictionary)

Salida:

ingrese la descripción de la imagen aquí


dump imprime el contenido de un objeto a través de la reflexión (espejo).

Vista detallada de una matriz:

let names = ["Joe", "Jane", "Jim", "Joyce"]
dump(names)

Huellas dactilares:

▿ 4 elementos
- [0]: Joe
- [1]: Jane
- [2]: Jim
- [3]: Joyce

Para un diccionario:

let attributes = ["foo": 10, "bar": 33, "baz": 42]
dump(attributes)

Huellas dactilares:

▿ 3 pares clave / valor
▿ [0]: (2 elementos)
- .0: bar
- .1: 33
▿ [1]: (2 elementos)
- .0: baz
- .1: 42
▿ [2]: ( 2 elementos)
- .0: foo
- .1: 10

dump se declara como dump(_:name:indent:maxDepth:maxItems:) .

El primer parámetro no tiene etiqueta.

Hay otros parámetros disponibles, como nameestablecer una etiqueta para el objeto que se inspecciona:

dump(attributes, name: "mirroring")

Huellas dactilares:

▿ espejo: 3 pares clave / valor
▿ [0]: (2 elementos)
- .0: bar
- .1: 33
▿ [1]: (2 elementos)
- .0: baz
- .1: 42
▿ [2] : (2 elementos)
- .0: foo
- .1: 10

También puede optar por imprimir solo un cierto número de elementos con maxItems:, analizar el objeto hasta una cierta profundidad maxDepth:y cambiar la sangría de los objetos impresos con indent:.

Eric Aya
fuente
5
Este no es un JSON bastante impreso, solo está volcando una variable en la consola, no es un JSON válido. Si bien se adapta a las necesidades de OP, creo que la pregunta necesita una nueva redacción para que coincida con esto.
James Wolfe
4
@JamesWolfe This is not pretty printed JSONNadie dijo que lo fuera. El OP preguntó sobre la impresión bonita de los diccionarios Swift: nadie está hablando de JSON, excepto algunos respondedores fuera de tema. La pregunta del OP no se trata de JSON en absoluto.
Eric Aya
@JamesWolfe Además, no cambie la pregunta. Eso sería vandalismo. La pregunta es clara como está, y no se trata de JSON. No cambie una pregunta solo porque algunas respuestas hablan de otra cosa. Gracias.
Eric Aya
112

Lanzar un diccionario a 'AnyObject' fue la solución más simple para mí:

let dictionary = ["a":"b",
                  "c":"d",
                  "e":"f"]
print("This is the console output: \(dictionary as AnyObject)")

esta es la salida de la consola

Esto es más fácil de leer para mí que la opción de volcado, pero tenga en cuenta que no le dará la cantidad total de valores-clave.

Jalakoo
fuente
11
Es una forma brillante y mucho mejor que la basura
AbdelHady
109

po solución

Para aquellos de ustedes que quieran ver el Diccionario como JSON sin secuencia de escape en la consola , aquí hay una forma sencilla de hacerlo

(lldb)p print(String(data: try! JSONSerialization.data(withJSONObject: object, options: .prettyPrinted), encoding: .utf8 )!)

Irshad Mohamed
fuente
1
Dado que es una expresión y no un objeto, debería ser 'p' y no 'po'. ¡Pero muchas gracias por esta solución! Funciona bien para mí
Alessandro Francucci
@AlessandroFrancucci ¿importa? El comando parece hacer lo mismo de cualquier manera.
nickjwallin
Ahora ambas formas de hacerlo están funcionando. Pero antes de hacer un "po print" no funcionó para mí. (po significa objeto de impresión ... que es un poco confuso si tiene una impresión después y no un objeto en mi humilde opinión)
Alessandro Francucci
¡Increíble! justo lo que necesitaba para imprimir de una manera agradable userInfo de PushNotification
carmen_munich
¡Consulte este comentario para aprovechar esto en un alias lldb para que no tenga que escribirlo todo el tiempo!
Agirault
36

Solo otra forma de usar la programación funcional

dictionary.forEach { print("\($0): \($1)") }

Salida

B: bravo
A: alfa
F: foxtrot
C: charlie
D: delta
E: echo
Luca Angeletti
fuente
1
Esta debería ser la mejor respuesta. ¡Funciona perfectamente!
Yuri Doubov
O para ser "aún más funcional" ... dictionary.map {"($ 0): ($ 1)"} .forEach (imprimir) (comentario irónico)
Jon Willis
3
Esto funciona para el [String: String]diccionario de OP , pero no es bueno para los [AnyHashable: Any]diccionarios, donde si un valor es un diccionario, vuelve a la impresión no bonita de Swift.
Christopher Pickslay
He marcado el libro esta respuesta book, porque todavía no puedo recordar esta sintaxis 🙄
Nitin Alabur
29

Solo para fines de depuración, convertiría la matriz o el diccionario en un json bastante impreso:

public extension Collection {

    /// Convert self to JSON String.
    /// Returns: the pretty printed JSON string or an empty string if any error occur.
    func json() -> String {
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted])
            return String(data: jsonData, encoding: .utf8) ?? "{}"
        } catch {
            print("json serialization error: \(error)")
            return "{}"
        }
    }
}

Entonces:

print("\nHTTP request: \(URL)\nParams: \(params.json())\n")

Resultado en consola:

HTTP request: https://example.com/get-data
Params: {
  "lon" : 10.8663676,
  "radius" : 111131.8046875,
  "lat" : 23.8063882,
  "index_start" : 0,
  "uid" : 1
}
Marco M
fuente
¿Qué es bLog aquí?
Nitesh
@Nitesh bLog es un registrador personalizado simple con retroceso que escribí, editado con print ().
Marco M
La solución más hermosa.
Denis Kutlubaev
Si desea evitar agregar ese fragmento de código en cada uno de sus proyectos, puede aprovechar ese código con un alias lldb para calcular fácilmente el json en la terminal de depuración (detalles aquí ).
agirault
14

No consideraría muchas de las respuestas proporcionadas aquí como verdaderas JSON bastante impresas, ya que cuando pasa los resultados a un validador JSON, el resultado no es válido (a menudo debido a que el código incluye '=' en lugar de ':').

La forma más fácil que he encontrado de hacer esto es simplemente convertir el objeto JSON en datos usando la opción de escritura bastante impresa y luego imprimir una cadena usando los datos resultantes.

Aquí hay un ejemplo:

let jsonData = try! JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)

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

Resultado:

{
    "jsonData": [
        "Some String"
    ],
    "moreJSONData": "Another String",
    "evenMoreJSONData": {
        "A final String": "awd"
    }
}

EDITAR : Se ha señalado que el OP no solicitó JSON, sin embargo, encuentro que las respuestas que recomiendan simplemente imprimir o volcar los datos en la consola brindan muy poco formato (si corresponde) y, por lo tanto, no son una impresión bonita.

Creo que a pesar de que el OP no solicita JSON, es una respuesta viable, ya que es un formato de datos mucho más legible que el horrendo formato que xcode / swift arroja a la consola.

James Wolfe
fuente
1
Gracias, con esto pude imprimir bastante en medio de la depuración a través de e let jsonData = try! JSONSerialization.data(withJSONObject: response, options: .prettyPrinted);if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) }
BangOperator
1
¡Esto es genial! Puede aprovechar este código con un alias lldb para calcular fácilmente el json en la terminal de depuración (detalles aquí ).
agirault
5

Puede usar un bucle for e imprimir cada iteración

for (key,value) in dictionary { 
    print("\(key) = \(value)")
}

Aplicación en extensión:

extension Dictionary where Key: CustomDebugStringConvertible, Value:CustomDebugStringConvertible {

    var prettyprint : String {
        for (key,value) in self {
            print("\(key) = \(value)")
        }

        return self.description
    }
}

Aplicación alternativa:

extension Dictionary where Key: CustomDebugStringConvertible, Value:CustomDebugStringConvertible {

    func prettyPrint(){
        for (key,value) in self {
            print("\(key) = \(value)")
        }
    }
}

Uso:

dictionary.prettyprint //var prettyprint
dictionary.prettyPrint //func prettyPrint

Salida (probado en Xcode 8 beta 2 Playground):

A = alfa
B = bravo
C = charlie
D = delta
E = echo
F = foxtrot
Asdrúbal
fuente
1
¿Hay alguna razón por la que hizo prettyprint una var en lugar de solo una función?
Hayden Holligan
Honestamente, no creo que importe (podría estar equivocado). Pero si lo usa mucho, es menos para escribir. Pero plantee una pregunta interesante.
Asdrúbal
3
Dado que ya existe un descriptiony debugDescription, podría ser más apropiado llamar a var prettyDescriptiony devolver la cadena formateada.
Toland Hon
5

La metodología para convertir el Diccionario Swift a json y viceversa es la mejor. Utilizo el cincel de Facebook que tiene un comando pjson para imprimir un diccionario Swift. P.ej:

(lldb) pjson dict as NSDictionary

Esto debería imprimir bastante el diccionario. Esta es una forma mucho más limpia de hacer lo que ya se ha sugerido. PD: Por ahora, tendrá que lanzar dict como NSDictionary porque el tiempo de ejecución de Objective-C no comprende los diccionarios Swift. Ya he planteado un PR sobre cincel para deshacerme de esa restricción.

ACTUALIZACIÓN: Mi PR fue aceptado. Ahora puede usar el comando psjson en lugar de pjson mencionado anteriormente.

jarora
fuente
4

Para Swift 3 (y basándose en la brillante respuesta de @Jalakoo ), cree la siguiente Dictionaryextensión:

extension Dictionary where Key: ExpressibleByStringLiteral, Value: Any {
    var prettyPrint: String {
        return String(describing: self as AnyObject)
    }
}

luego imprima un diccionario de cualquier jerarquía de una manera bonita (mejor que dump()) usando esto:

print(dictionary!.prettyPrint)
AbdelHady
fuente
4

Detalles

  • Xcode 10.2.1 (10E1001), Swift 5

Solución

extension Dictionary {
    func format(options: JSONSerialization.WritingOptions) -> Any? {
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: self, options: options)
            return try JSONSerialization.jsonObject(with: jsonData, options: [.allowFragments])
        } catch {
            print(error.localizedDescription)
            return nil
        }
    }
}

Uso

let dictionary: [String : Any] = [
                                    "id": 0,
                                    "bool": true,
                                    "int_array": [1,3,5],
                                    "dict_array": [
                                        ["id": 1, "text": "text1"],
                                        ["id": 1, "text": "text2"]
                                    ]
                                 ]
print("Regualr print:\n\(dictionary)\n")
guard let formatedDictionary = dictionary.format(options: [.prettyPrinted, .sortedKeys]) else { return }
print("Pretty printed:\n\(formatedDictionary)\n")

Resultados

ingrese la descripción de la imagen aquí

Vasily Bodnarchuk
fuente
2

Ajustado según mi otra respuesta aquí .

Solución PrettyPrint JSON con alias LLDB

No se necesita código

  • Para obtener un buen formato json (sangrías, líneas nuevas, etc.), puede definir un alias lldb ejecutando este comando en su terminal lldb ( fuente ):
command regex pjson 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'
  • Probablemente no quieras volver a definir el alias cada vez que abras XCode, así que ejecuta el siguiente comando para agregar la definición de alias a ~/.lldbinit:
echo "command regex pjson 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'" >> ~/.lldbinit
  • Esto creará el pjsonalias que puede usar en su terminal lldb en XCode:
pjson object

Comparando las salidas para el siguiente objeto Swift:

// Using Any? to demo optional & arbitrary Type
let dictionary: Any? = [
    "embedded": [
        "JustForTheSakeOfTheDemo": 42
    ],
    "A" : "alfa",
    "B" : "bravo",
    "C" : "charlie",
    "D" : "delta",
    "E" : "echo",
    "F" : "foxtrot"
]

✅ Salida de pjson dictionary

{
  "F" : "foxtrot",
  "D" : "delta",
  "embedded" : {
    "JustForTheSakeOfTheDemo" : 42
  },
  "E" : "echo",
  "A" : "alfa",
  "C" : "charlie",
  "B" : "bravo"
}

❌ Salida de p dictionary

(Any?) $R0 = 7 key/value pairs {
  [0] = {
    key = "F"
    value = "foxtrot"
  }
  [1] = {
    key = "D"
    value = "delta"
  }
  [2] = {
    key = "embedded"
    value = 1 key/value pair {
      [0] = (key = "JustForTheSakeOfTheDemo", value = 42)
    }
  }
  [3] = {
    key = "E"
    value = "echo"
  }
  [4] = {
    key = "A"
    value = "alfa"
  }
  [5] = {
    key = "C"
    value = "charlie"
  }
  [6] = {
    key = "B"
    value = "bravo"
  }
}

❌ Salida de p (dictionary as! NSDictionary)

(NSDictionary) $R18 = 0x0000000281e89710 {
  ObjectiveC.NSObject = {
    base__SwiftNativeNSDictionaryBase@0 = {
      baseNSDictionary@0 = {
        NSObject = {
          isa = Swift._SwiftDeferredNSDictionary<Swift.String, Any> with unmangled suffix "$"
        }
      }
    }
  }
}

❌ Salida de po dictionary

Optional<Any>
  ▿ some : 7 elements
    ▿ 0 : 2 elements
      - key : "F"
      - value : "foxtrot"1 : 2 elements
      - key : "D"
      - value : "delta"2 : 2 elements
      - key : "embedded"
      ▿ value : 1 element
        ▿ 0 : 2 elements
          - key : "JustForTheSakeOfTheDemo"
          - value : 423 : 2 elements
      - key : "E"
      - value : "echo"4 : 2 elements
      - key : "A"
      - value : "alfa"5 : 2 elements
      - key : "C"
      - value : "charlie"6 : 2 elements
      - key : "B"
      - value : "bravo"

❌ Salida de po print(dictionary)

Optional(["F": "foxtrot", "D": "delta", "embedded": ["JustForTheSakeOfTheDemo": 42], "E": "echo", "A": "alfa", "C": "charlie", "B": "bravo"])
agirault
fuente
1

swift 5, xcode 10.3:

po print(<your Plist container>)
Sobre Ucrania
fuente
1

Al depurar, genere la estructura que se ajusta al Protocolo de codificación en la consola con el
formato json.

extension Encodable {
    var jsonData: Data? {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        return try? encoder.encode(self)
    }
}

extension Encodable where Self: CustomDebugStringConvertible {
    var debugDescription: String {
         if let data = self.jsonData,
             let string = String(data: data, encoding: .utf8) {
             return string
         }
         return "can not convert to json string"
     }
}

strcut conforma CustomDebugStringConvertible

struct Test: Codable, CustomDebugStringConvertible {
    let a: String
    let b: Int
}

let t = Test(a: "test string", b: 30)

estructura de impresión de depuración

(lldb) p print(t)
{
  "a" : "test string",
  "b" : 30
}
wlixcc
fuente
1

Impresión bonita del objeto de datos:

let jsonObj = try JSONSerialization.jsonObject(with: data, options: [])
            let jsonData = try JSONSerialization.data(withJSONObject: jsonObj, options: [.prettyPrinted])
            print(String(data: jsonData, encoding: .utf8)!)
Hugo Jordao
fuente
1
¡Esto es genial! Puede aprovechar este código con un alias lldb para calcular fácilmente el json en la terminal de depuración (detalles aquí ).
agirault
0

Qué tal si:

import Foundation

extension Dictionary {
    var myDesc: String {
        get {
            var v = ""
            for (key, value) in self {
                v += ("\(key) = \(value)\n")
            }
            return v
        }
    }
}


// Then, later, for any dictionary:
print(dictionary.myDesc)
BaseZen
fuente
0
extension String {

    var conslePrintString: String {

        guard let data = "\""
            .appending(
                replacingOccurrences(of: "\\u", with: "\\U")
                    .replacingOccurrences(of: "\"", with: "\\\"")
            )
            .appending("\"")
            .data(using: .utf8) else {

            return self
        }

        guard let propertyList = try? PropertyListSerialization.propertyList(from: data,
                                                                             options: [],
                                                                             format: nil) else {
            return self
        }

        guard let string = propertyList as? String else {
            return self
        }

        return string.replacingOccurrences(of: "\\r\\n", with: "\n")
    }
}

let code in extension String and it works fine 

let string = "\(jsonDictionary)".conslePrintString
hasayakey
fuente