Existe-t-il un moyen d'imprimer de jolis dictionnaires Swift sur la console?

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

imprime ce qui suit sur la console:

{
    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 ce qui suit sur la console:

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

Existe-t-il un moyen dans Swift d'obtenir de jolis dictionnaires imprimés où chaque paire clé-valeur occupe une nouvelle ligne?

Toland Hon
la source
7
Vous pouvez utiliser dump, par exemple, si l'objectif est d'inspecter le dictionnaire. stackoverflow.com/documentation/swift/3966/logging-in-swift/…
Eric Aya
13
print(dictionary as! NSDictionary) truc pas cher?
BaseZen
J'ai vraiment la suggestion de dump () car il ne nécessite pas d'écrire de code ou de le convertir. @EricAya, si vous postez une réponse avec cette remarque, je la marquerai comme réponse.
Toland Hon
1
@TolandHon fait. J'ai fait une réponse avec un exemple de la sortie.
Eric Aya

Réponses:

99

Vous pouvez utiliser dump , par exemple, si l'objectif est d'inspecter le dictionnaire. dumpfait partie de la bibliothèque standard de Swift.

Usage:

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

dump(dictionary)

Production:

entrez la description de l'image ici


dump imprime le contenu d'un objet par réflexion (mise en miroir).

Vue détaillée d'un tableau:

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

Impressions:

▿ 4 éléments
- [0]: Joe
- [1]: Jane
- [2]: Jim
- [3]: Joyce

Pour un dictionnaire:

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

Impressions:

▿ 3 paires clé / valeur
▿ [0]: (2 éléments)
- .0: bar
- .1: 33
▿ [1]: (2 éléments)
- .0: baz
- .1: 42
▿ [2]: ( 2 éléments)
- .0: foo
- .1: 10

dumpest déclaré comme dump(_:name:indent:maxDepth:maxItems:).

Le premier paramètre n'a pas d'étiquette.

Il existe d'autres paramètres disponibles, comme namedéfinir une étiquette pour l'objet inspecté:

dump(attributes, name: "mirroring")

Impressions:

▿ mise en miroir: 3 paires clé / valeur
▿ [0]: (2 éléments)
- .0: bar
- .1: 33
▿ [1]: (2 éléments)
- .0: baz
- .1: 42
▿ [2] : (2 éléments)
- .0: foo
- .1: 10

Vous pouvez également choisir de n'imprimer qu'un certain nombre d'éléments avec maxItems:, d'analyser l'objet jusqu'à une certaine profondeur avec maxDepth:et de modifier l'indentation des objets imprimés avec indent:.

Eric Aya
la source
5
Ce n'est pas un JSON assez imprimé, il s'agit simplement de vider une variable dans la console - pas de JSON valide. Bien que cela réponde aux besoins d'OP, je pense que la question doit être reformulée pour correspondre à cela.
James Wolfe
4
@JamesWolfe This is not pretty printed JSONPersonne n'a dit que c'était le cas. L'OP a posé des questions sur les jolis dictionnaires Swift d'impression - personne ne parle de JSON, à l'exception de quelques répondeurs hors sujet. La question de l'OP ne concerne pas du tout JSON.
Eric Aya
@JamesWolfe Veuillez également ne pas modifier la question. Ce serait du vandalisme. La question est claire telle qu'elle est, et il ne s'agit pas de JSON. Ne changez pas une question simplement parce que certaines réponses parlent d'autre chose. Merci.
Eric Aya
112

Casting d'un dictionnaire en 'AnyObject' était la solution la plus simple pour moi:

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

c'est la sortie de la console

C'est plus facile à lire pour moi que l'option de vidage, mais notez qu'elle ne vous donnera pas le nombre total de paires valeur / clé.

Jalakoo
la source
11
C'est une manière brillante et bien meilleure que dump
AbdelHady
109

solution po

Pour ceux d'entre vous qui veulent voir Dictionary comme JSON sans séquence d'échappement dans la console , voici un moyen simple de le faire

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

Irshad Mohamed
la source
1
Puisqu'il s'agit d'une expression et non d'un objet, ce devrait être «p» et non «po». Mais merci beaucoup pour cette solution! Fonctionne bien pour moi
Alessandro Francucci
@AlessandroFrancucci est-ce important? La commande semble faire la même chose dans les deux cas.
nickjwallin
Maintenant, les deux méthodes fonctionnent. Mais avant de faire un "po print" ne fonctionnait pas pour moi. (po signifie objet d'impression .... ce qui est un peu déroutant si vous avez une impression après et pas un objet à mon humble avis)
Alessandro Francucci
Impressionnant! juste ce dont j'avais besoin pour imprimer de manière agréable userInfo de PushNotification
carmen_munich
Vérifiez ce commentaire pour en tirer parti dans un alias lldb afin de ne pas avoir à le taper à chaque fois!
agirault le
36

Juste une autre façon d'utiliser la programmation fonctionnelle

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

Production

B: bravo
A: alfa
F: foxtrot
C: charlie
D: delta
E: echo
Luca Angeletti
la source
1
Cela devrait être la meilleure réponse. Fonctionne parfaitement!
Yuri Doubov
Ou pour être "encore plus fonctionnel" ... dictionary.map {"($ 0): ($ 1)"} .forEach (impression) (commentaire ironique)
Jon Willis
3
Cela fonctionne pour le [String: String]dictionnaire d'OP , mais ce n'est pas génial pour les [AnyHashable: Any]dictionnaires, où si une valeur est un dictionnaire, vous revenez à l'impression non jolie de Swift.
Christopher Pickslay le
J'ai un livre marqué cette réponse🙂, car je ne me souviens toujours pas de cette syntaxe 🙄
Nitin Alabur
29

À des fins de débogage uniquement, je convertirais le tableau ou le dictionnaire en un joli json imprimé:

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 "{}"
        }
    }
}

Ensuite:

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

Résultat sur console:

HTTP request: https://example.com/get-data
Params: {
  "lon" : 10.8663676,
  "radius" : 111131.8046875,
  "lat" : 23.8063882,
  "index_start" : 0,
  "uid" : 1
}
Marco M
la source
qu'est-ce que bLog ici?
Nitesh le
@Nitesh bLog est un simple enregistreur personnalisé avec backtrace que j'ai écrit, édité avec print ().
Marco M
La plus belle solution.
Denis Kutlubaev le
Si vous souhaitez éviter d'ajouter cet extrait de code dans chacun de vos projets, vous pouvez utiliser ce code avec un alias lldb pour calculer facilement le json dans le terminal de débogage (détails ici ).
agirault le
14

Je ne considérerais pas une grande partie des réponses fournies ici comme de vrais JSON imprimés, car lorsque vous transmettez les résultats à un validateur JSON, le résultat est invalide (souvent en raison du code comprenant '=' plutôt que ':').

Le moyen le plus simple que j'ai trouvé de faire cela consiste simplement à convertir l'objet JSON en données à l'aide de l'option d'écriture jolie imprimée, puis à imprimer une chaîne en utilisant les données résultantes.

Voici un exemple:

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

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

Résultat:

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

EDIT : Il a été souligné que l'OP n'a pas demandé JSON, mais je trouve que les réponses qui recommandent simplement d'imprimer ou de transférer les données dans la console fournissent très peu de formatage (le cas échéant) et ne sont donc pas assez imprimables.

Je crois que bien que l'OP ne demande pas JSON, c'est une réponse viable car il s'agit d'un format de données beaucoup plus lisible que le format horrible qui est craché dans la console par xcode / swift.

James Wolfe
la source
1
Merci, avec cela, j'ai pu imprimer assez au milieu du débogage via e let jsonData = try! JSONSerialization.data(withJSONObject: response, options: .prettyPrinted);if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) }
BangOperator
1
C'est bien! Vous pouvez utiliser ce code avec un alias lldb pour calculer facilement le json dans le terminal de débogage (détails ici ).
agirault le
5

Vous pouvez simplement utiliser une boucle for et imprimer chaque itération

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

Application en extension:

extension Dictionary where Key: CustomDebugStringConvertible, Value:CustomDebugStringConvertible {

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

        return self.description
    }
}

Autre application:

extension Dictionary where Key: CustomDebugStringConvertible, Value:CustomDebugStringConvertible {

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

Usage:

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

Sortie (testé dans Xcode 8 beta 2 Playground):

A = alfa
B = bravo
C = charlie
D = delta
E = echo
F = foxtrot
Asdrubal
la source
1
Y a-t-il une raison pour laquelle vous avez fait de prettyprint une variable au lieu d'une simple fonction?
Hayden Holligan
Honnêtement, je ne pense pas que ce soit important (je peux me tromper). Mais si vous l'utilisez beaucoup, c'est moins à taper. Mais soulevez une question intéressante.
Asdrubal
3
Comme il existe déjà un descriptionet debugDescription, il peut être plus approprié d'appeler le var prettyDescriptionet de renvoyer la chaîne formatée.
Toland Hon
5

La méthodologie de conversion du dictionnaire Swift en json et inversement est la plus soignée. J'utilise le ciseau de Facebook qui a une commande pjson pour imprimer un dictionnaire Swift. Par exemple:

(lldb) pjson dict as NSDictionary

Cela devrait bien imprimer le dictionnaire. C'est une manière beaucoup plus propre de faire ce qui a déjà été suggéré. PS Pour l'instant, vous devrez convertir dict en NSDictionary car le runtime Objective-C ne comprend pas les dictionnaires Swift. J'ai déjà soulevé un PR sur le ciseau pour me débarrasser de cette restriction.

MISE À JOUR: Mon PR a été accepté. Vous pouvez maintenant utiliser la commande psjson au lieu de pjson mentionné ci-dessus.

Jarora
la source
4

Pour Swift 3 (et en vous appuyant sur la brillante réponse de @Jalakoo ), créez l' Dictionaryextension suivante :

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

puis imprimez un dictionnaire de n'importe quelle hiérarchie d'une jolie manière (mieux que dump()) en utilisant ceci:

print(dictionary!.prettyPrint)
AbdelHady
la source
4

Détails

  • Xcode 10.2.1 (10E1001), Swift 5

Solution

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

Usage

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

Résultats

entrez la description de l'image ici

Vasily Bodnarchuk
la source
2

Ajusté en fonction de mon autre réponse ici .

Solution JSON PrettyPrint utilisant l'alias LLDB

Aucun code nécessaire

  • Pour obtenir un bon formatage json (indentations, retours à la ligne, etc.) vous pouvez définir un alias lldb en exécutant cette commande dans votre terminal lldb ( source ):
command regex pjson 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'
  • Vous ne souhaitez probablement pas redéfinir l'alias à chaque fois que vous ouvrez XCode, alors exécutez la commande suivante pour ajouter la définition d'alias à ~/.lldbinit:
echo "command regex pjson 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'" >> ~/.lldbinit
  • Cela créera l' pjsonalias que vous pouvez utiliser dans votre terminal lldb dans XCode:
pjson object

Comparaison des sorties pour l'objet Swift suivant:

// 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"
]

✅ Sortie de pjson dictionary

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

❌ Sortie 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"
  }
}

❌ Sortie 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 "$"
        }
      }
    }
  }
}

❌ Sortie 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"

❌ Sortie de po print(dictionary)

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

swift 5, xcode 10.3:

po print(<your Plist container>)
Sur l'Ukraine
la source
1

Lors du débogage, affichez la structure conforme au protocole codable sur la console en
utilisant le format 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 conforme CustomDebugStringConvertible

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

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

debug print struct

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

Jolie impression à partir de l'objet de données:

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
la source
1
C'est bien! Vous pouvez utiliser ce code avec un alias lldb pour calculer facilement le json dans le terminal de débogage (détails ici ).
agirault le
0

Que diriez-vous:

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
la source
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
la source