Czy istnieje sposób, aby ładnie wydrukować słowniki Swift na konsoli?


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

wypisze na konsoli następujące informacje:

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

wypisze na konsoli następujące informacje:

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

Czy w języku Swift jest sposób, aby umieścić go w ładnych drukowanych słownikach, w których każda para klucz-wartość zajmuje nową linię?


7
Możesz dumpna przykład użyć , jeśli celem jest sprawdzenie słownika. stackoverflow.com/documentation/swift/3966/logging-in-swift/…
Eric Aya

13
print(dictionary as! NSDictionary) tania sztuczka?
BaseZen

Naprawdę mam sugestię dump (), ponieważ nie wymaga ona pisania żadnego kodu ani przesyłania go. @EricAya, jeśli opublikujesz odpowiedź z tą uwagą, oznaczę ją jako odpowiedź.
Toland Hon

1
@TolandHon Gotowe. Uczyniłem odpowiedź na przykładzie wyniku.
Eric Aya

Odpowiedzi:


99

Możesz na przykład użyć zrzutu , jeśli celem jest sprawdzenie słownika. dumpjest częścią standardowej biblioteki Swift.

Stosowanie:

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

dump(dictionary)

Wynik:

wprowadź opis obrazu tutaj


dump drukuje zawartość obiektu poprzez odbicie (odbicie lustrzane).

Szczegółowy widok tablicy:

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

Wydruki:

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

W przypadku słownika:

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

Wydruki:

▿ 3 pary klucz / wartość
▿ [0]: (2 elementy)
- .0: bar
- .1: 33
▿ [1]: (2 elementy)
- .0: baz
- .1: 42
▿ [2]: ( 2 elementy)
- .0: foo
- .1: 10

dumpjest zadeklarowany jako dump(_:name:indent:maxDepth:maxItems:).

Pierwszy parametr nie ma etykiety.

Dostępne są inne parametry, takie jak nameustawienie etykiety dla sprawdzanego obiektu:

dump(attributes, name: "mirroring")

Wydruki:

▿ odbicie lustrzane: 3 pary klucz / wartość
▿ [0]: (2 elementy)
- .0: bar
- .1: 33
▿ [1]: (2 elementy)
- .0: baz
- .1: 42
▿ [2] : (2 elementy)
- .0: foo
- .1: 10

Możesz również wybrać drukowanie tylko określonej liczby elementów maxItems:, analizować obiekt do określonej głębokości za pomocą maxDepth:, a także zmieniać wcięcia drukowanych obiektów za pomocą indent:.


5
To nie jest ładnie wydrukowany JSON, to po prostu zrzucenie zmiennej do konsoli - nie jest to poprawny JSON. Chociaż pasuje do potrzeb OP, uważam, że pytanie wymaga przeformułowania, aby pasowało do tego.
James Wolfe

4
@JamesWolfe This is not pretty printed JSONNikt nie powiedział, że tak. OP zapytał o ładne drukowanie słowników Swift - nikt nie mówi o JSON, z wyjątkiem kilku osób odpowiadających na nie na temat. Quesiton OP w ogóle nie dotyczy JSON.
Eric Aya

@JamesWolfe Również proszę nie zmieniać pytania. To byłby wandalizm. Pytanie jest jasne i nie chodzi o JSON. Nie zmieniaj pytania tylko dlatego, że niektóre odpowiedzi mówią o czymś innym. Dzięki.
Eric Aya

112

Przesyłanie słownika do „AnyObject” było dla mnie najprostszym rozwiązaniem:

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

to jest wyjście konsoli

Jest to dla mnie łatwiejsze do odczytania niż opcja zrzutu, ale pamiętaj, że nie poda całkowitej liczby par klucz-wartość.


11
To genialny sposób i znacznie lepszy niż wysypisko
AbdelHady,

109

rozwiązanie po

Dla tych z Was, którzy chcą widzieć Dictionary jako JSON bez sekwencji ucieczki w konsoli , oto prosty sposób na zrobienie tego

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


1
Ponieważ jest to wyrażenie, a nie obiekt, powinno to być „p”, a nie „po”. Ale bardzo dziękuję za to rozwiązanie! U mnie działa dobrze
Alessandro Francucci

@AlessandroFrancucci czy to ma znaczenie? Wydaje się, że polecenie robi to samo w obu przypadkach.
nickjwallin

Teraz oba sposoby na to działają. Ale przed zrobieniem "wydruku po" nie działało dla mnie. (po oznacza drukowany obiekt .... co jest nieco mylące, jeśli masz później wydruk, a nie obiekt imho)
Alessandro Francucci

Niesamowite! tylko to, czego potrzebowałem, aby wydrukować w ładny sposób userInfo z PushNotification
carmen_munich

Czek ten komentarz, aby wykorzystać go w aliasie lldb, więc nie musisz go wpisywać za każdym razem!
agirault

36

Po prostu inny sposób korzystania z programowania funkcyjnego

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

Wynik

B: bravo
A: alfa
F: foxtrot
C: charlie
D: delta
E: echo

1
To powinna być najlepsza odpowiedź. Działa świetnie!
Yuri Doubov

Albo żeby być „jeszcze bardziej funkcjonalnym” ... Dictionary.map {"($ 0): ($ 1)"} .forEach (print) (żartobliwy komentarz)
Jon Willis

3
Działa to w przypadku [String: String]słownika OP , ale nie jest dobre dla [AnyHashable: Any]słowników, w których jeśli wartość jest słownikiem, wrócisz do niezbyt ładnego drukowania Swift.
Christopher Pickslay

Oznaczyłem książkę oznaczoną tą odpowiedzią🙂, bo nadal nie pamiętam tej składni 🙄
Nitin Alabur

29

Tylko w celu debugowania przekonwertowałbym tablicę lub słownik na ładnie wydrukowany plik json:

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

Następnie:

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

Wynik na konsoli:

HTTP request: https://example.com/get-data
Params: {
  "lon" : 10.8663676,
  "radius" : 111131.8046875,
  "lat" : 23.8063882,
  "index_start" : 0,
  "uid" : 1
}

co tu jest bLog?
Nitesh

@Nitesh bLog to prosty niestandardowy rejestrator z zapisem wstecznym, który napisałem, edytowany za pomocą print ().
Marco M

Najpiękniejsze rozwiązanie.
Denis Kutlubaev

Jeśli chcesz uniknąć dodawania tego fragmentu kodu w każdym ze swoich projektów, możesz wykorzystać ten kod z aliasem lldb, aby łatwo obliczyć plik JSON w terminalu debugowania (szczegóły tutaj ).
agirault

14

Nie uważałbym wielu z podanych tutaj odpowiedzi za prawdziwe, dobrze wydrukowane JSON, ponieważ gdy przekazujesz wyniki do walidatora JSON, wynik jest nieprawidłowy (często z powodu kodu zawierającego „=” zamiast „:”).

Najłatwiejszym sposobem, jaki znalazłem, jest po prostu przekonwertowanie obiektu JSON na dane przy użyciu ładnie wydrukowanej opcji zapisu, a następnie wydrukowanie ciągu przy użyciu wynikowych danych.

Oto przykład:

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

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

Wynik:

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

EDYCJA : Zwrócono uwagę, że OP nie pytał o JSON, jednak uważam, że odpowiedzi, które zalecają po prostu drukowanie lub zrzucanie danych do konsoli, zapewniają bardzo małe formatowanie (jeśli w ogóle) i dlatego nie są ładne do wydrukowania.

Uważam, że pomimo tego, że OP nie pyta o JSON, jest to realna odpowiedź, ponieważ jest to znacznie bardziej czytelny format danych niż horrendalny format wypluwany do konsoli przez xcode / swift.


1
Dzięki, dzięki temu mogłem ładnie drukować wśród debugowania za pośrednictwem e let jsonData = try! JSONSerialization.data(withJSONObject: response, options: .prettyPrinted);if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) }
BangOperator

1
To jest świetne! Możesz wykorzystać ten kod z aliasem lldb, aby łatwo obliczyć json w terminalu debugowania (szczegóły tutaj ).
agirault

5

Możesz po prostu użyć pętli for i wydrukować każdą iterację

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

Wniosek w przedłużeniu:

extension Dictionary where Key: CustomDebugStringConvertible, Value:CustomDebugStringConvertible {

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

        return self.description
    }
}

Alternatywna aplikacja:

extension Dictionary where Key: CustomDebugStringConvertible, Value:CustomDebugStringConvertible {

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

Stosowanie:

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

Dane wyjściowe (testowane w Xcode 8 beta 2 Playground):

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

1
Czy jest jakiś powód, dla którego utworzyłeś prettyprint jako zmienną zamiast zwykłej funkcji?
Hayden Holligan

Szczerze mówiąc, nie sądzę, żeby to miało znaczenie (mogę się mylić). Ale jeśli używasz go dużo, mniej wpisujesz. Ale zadaj interesujące pytanie.
Asdrubal

3
Ponieważ istnieje descriptioni debugDescriptionjuż, bardziej odpowiednie może być wywołanie zmiennej prettyDescriptioni zwrócenie sformatowanego ciągu.
Toland Hon

5

Metodologia konwersji Swift Dictionary do formatu json iz powrotem jest najdoskonalsza. Używam dłuta Facebooka, który ma polecenie pjson, aby wydrukować słownik Swift. Na przykład:

(lldb) pjson dict as NSDictionary

Powinno to ładnie wydrukować słownik. Jest to znacznie czystszy sposób wykonania tego, co już zostało zasugerowane. PS Na razie musisz rzutować dict jako NSDictionary, ponieważ środowisko wykonawcze Objective-C nie rozumie słowników Swift. Podniosłem już PR dotyczący dłuta, aby pozbyć się tego ograniczenia.

UPDATE: Mój PR został zaakceptowany. Teraz możesz użyć polecenia psjson zamiast wspomnianego powyżej pjson .


4

Dla Swift 3 (i opierając się na genialnej odpowiedzi @Jalakoo ), wprowadź następujące Dictionaryrozszerzenie:

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

następnie wypisz słownik dowolnej hierarchii w ładny sposób (lepszy niż dump()), używając tego:

print(dictionary!.prettyPrint)

4

Detale

  • Xcode 10.2.1 (10E1001), Swift 5

Rozwiązanie

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

Stosowanie

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

Wyniki

wprowadź opis obrazu tutaj


2

Dostosowano na podstawie mojej drugiej odpowiedzi tutaj .

Rozwiązanie PrettyPrint JSON używające aliasu LLDB

Nie potrzeba kodu

  • Aby uzyskać ładne formatowanie json (wcięcia, znaki nowej linii itp.), Możesz zdefiniować alias lldb, uruchamiając to polecenie w terminalu lldb ( źródło ):
command regex pjson 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'
  • Prawdopodobnie nie chcesz ponownie definiować aliasu za każdym razem, gdy otwierasz XCode, więc uruchom następujące polecenie, aby dołączyć definicję aliasu do ~/.lldbinit:
echo "command regex pjson 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'" >> ~/.lldbinit
  • Spowoduje to utworzenie pjsonaliasu, którego możesz użyć w terminalu lldb w XCode:
pjson object

Porównanie wyników dla następującego obiektu 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"
]

✅ Wyjście pjson dictionary

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

❌ Wyjście 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"
  }
}

❌ Wyjście p (dictionary as! NSDictionary)

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

❌ Wyjście 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"

❌ Wyjście po print(dictionary)

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


1

Podczas debugowania wyprowadzaj strukturę, która jest zgodna z protokołem Codable Protocol do konsoli,
użyj formatu 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 jest zgodny z CustomDebugStringConvertible

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

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

debugowanie print struct

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

1

Ładny wydruk z obiektu Data:

let jsonObj = try JSONSerialization.jsonObject(with: data, options: [])
            let jsonData = try JSONSerialization.data(withJSONObject: jsonObj, options: [.prettyPrinted])
            print(String(data: jsonData, encoding: .utf8)!)

1
To jest świetne! Możesz wykorzystać ten kod z aliasem lldb, aby łatwo obliczyć json w terminalu debugowania (szczegóły tutaj ).
agirault

0

Co powiesz na:

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)

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.