29

I have json data comes server side.

In case I use the next code I get not pretty printed one line string:

print(String(bytes: jsonData, encoding: String.Encoding.utf8))

To make it pretty printed I use the next code:

if let json = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) {
   if let prettyPrintedData = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) {
      print(String(bytes: prettyPrintedData, encoding: String.Encoding.utf8) ?? "NIL")
   }
}

But seems that it isn't the best way.

So does anybody know how to pretty print incoming jsonData to print it?

Dmitry
  • 2,963
  • 2
  • 21
  • 39
  • "But seems that it isn't the best way." what's wrong with it, exactly? – Alexander Apr 30 '18 at 22:48
  • @Alexander, Wrong that this code converts jsonData to jsonObject than converts jsonObject to jsonData. It is like a work which needs to be done twice. – Dmitry Apr 30 '18 at 22:51
  • Where does `jsonData` come from originally? At the place you need it pretty printed, do you have the source object it was generated from? – Alexander Apr 30 '18 at 22:52
  • @Alexander, I use Alamofire to send a request. The response contains json data. So I would like to print it right after the app received the response – Dmitry Apr 30 '18 at 22:55
  • Pretty printing requires placing white space in all the right places. Determining what all the right places are is pretty much the same as deserializing JSON, the only difference being that you don't need to interpret the parsed fields to build an object graph. But that doesn't take too much time, so it would be silly to write a separate JSON parser that parses the string solely for the purpose of pretty printing – Alexander Apr 30 '18 at 22:55
  • FYI - the use of `.mutableContainers` is pointless in Swift and really pointless just for the sake of pretty printing. – rmaddy Apr 30 '18 at 22:57
  • @Alexander, thanks for clarifying. – Dmitry Apr 30 '18 at 23:01
  • @rmaddy, which reading option you suggest to use? – Dmitry Apr 30 '18 at 23:02
  • Since you need to remove the only specified option, you would specify no options. `..., options: [])`. And since that is the default, simply leave off the `options:` parameter. – rmaddy Apr 30 '18 at 23:04
  • @rmaddy, thanks. It is really logical) – Dmitry Apr 30 '18 at 23:06
  • @Dmitry I would try to make use of an already-deserailized data, and reserialize it to JSON with the pretty printing option enabled, *if* you really need JSON. Otherwise, I think it's better to just `dump` the deserialized object. Also, I'd recommend using `Codable` instead of `JSONSerialization` – Alexander Apr 30 '18 at 23:09
  • @Alexander, I prefer using Codable but I'm working on the project written few years ago, so it can takes some time to rewrite deserialization logic – Dmitry Apr 30 '18 at 23:31

7 Answers7

40

A slightly prettier version of what you have:

if let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers),
   let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) {
    print(String(decoding: jsonData, as: UTF8.self))
} else {
    print("json data malformed")
}
Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
Elijah
  • 8,381
  • 2
  • 55
  • 49
10

Basically in Swift exists 2 types for strings: String and NSString

The second one is older (used even in Objective-C) and nowadays more often is used newer String type. However, NSString allows you to pretty print existing strings, which means that f.ex. backslashes will be deleted automatically.

Json received as Data from server

For this case I use such helper:

extension Data {
    var prettyString: NSString? {
        return NSString(data: self, encoding: String.Encoding.utf8.rawValue) ?? nil
    }
}

It's very important to use NSString over String type.

Alternatively you can operate on String, but in the end cast it to AnyObject, like below:

String(data: stringData, encoding: .utf8)! as AnyObject

The result in console looks like this (example of server response):

{
  "message" : "User could not log in",
  "errorCode" : "USER_COULD_NOT_LOG_IN",
  "parameters" : null
}

Pretty string for existing string

To prettify any string you can use second method from above, so cast String to AnyObject. It will also make string looking better. F.ex.:

let myStr: String = ...
print(myStr as AnyObject)
lukszar
  • 1,252
  • 10
  • 13
9

Use the output formatting option on the encoder directly:

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
mnl
  • 411
  • 5
  • 16
  • This only works if your side is doing the encoding. From his other comments, he's getting something from the server so he doesn't have that option. – Mark A. Donohoe Jul 13 '20 at 18:09
  • 2
    Although this might answer another question, this solved exactly what I googled for. – laka Jan 12 '21 at 13:12
4

Swift 5 and above

handy extension on Data to pretty-print JSON

extension Data {
    
    func printJson() {
        do {
            let json = try JSONSerialization.jsonObject(with: self, options: [])
            let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
            guard let jsonString = String(data: data, encoding: .utf8) else {
                print("Inavlid data")
                return
            }
            print(jsonString)
        } catch {
            print("Error: \(error.localizedDescription)")
        }
    }
}

How to Use:

 let json = """
  {
    "id": 1,
    "name": "Steve Jobs",
    "username": "Steve",
    "email": "jobs@apple.com",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "San Jose",
      "zipcode": "1234-5678"
    },
    "phone": "1-770-736-8031 x56442"
  }
"""

let jsonData = Data(json.utf8)
// log json data in pretty format
jsonData.printJson()
Suhit Patil
  • 11,748
  • 3
  • 50
  • 60
2

Data extension:

extension Data {
    
    func printFormatedJSON() {
        if let json = try? JSONSerialization.jsonObject(with: self, options: .mutableContainers),
           let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) {
            pringJSONData(jsonData)
        } else {
            assertionFailure("Malformed JSON")
        }
    }
    
    func printJSON() {
        pringJSONData(self)
    }
    
    private func pringJSONData(_ data: Data) {
        print(String(decoding: data, as: UTF8.self))
    }
}

Usage:

data.printFormatedJSON()
sash
  • 8,423
  • 5
  • 63
  • 74
0

here's the answer.

language: Objective-C


NSString+PrettyPrint.h

@interface NSString (PrettyPrint)

+ (NSString * _Nonnull)prettifiedJsonStringFromData:(nullable NSData *)data;
+ (NSString * _Nonnull)prettifiedStringFromDictionary:(nullable NSDictionary *)dictionary;

@end

NSString+PrettyPrint.m

#import "NSString+PrettyPrint.h"

@implementation NSString (PrettyPrint)

+ (NSString *)prettifiedStringFromDictionary:(nullable NSDictionary *)dictionary {
    
    if (dictionary == nil) { return @"nil"; }
    
    NSMutableString *returnStr = [NSMutableString stringWithString:@"[ \n"];
    
    for (NSString *key in dictionary) {
        [returnStr appendFormat:@"  %@: %@,\n", key, [dictionary valueForKey:key]];
    }

    [returnStr appendFormat:@"]"];

    return returnStr;
}

+ (NSString *)prettifiedJsonStringFromData:(nullable NSData *)data {
    
    if (data == nil) { return @"nil"; }
    
    NSData *jsonData;
    NSError *error = nil;
    
    NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
    id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:&error];
    if (jsonObject == nil) {
        return @"nil (json object from data)";
    } else {
        BOOL isValidJsonObject = [NSJSONSerialization isValidJSONObject:jsonObject];
        if (isValidJsonObject) {
            NSData *finalData = [NSJSONSerialization dataWithJSONObject:jsonObject options:NSJSONWritingPrettyPrinted error:&error];
            //TODO: error description
            NSString *prettyJson = [[NSString alloc] initWithData:finalData encoding:NSUTF8StringEncoding];
            return prettyJson;
        } else {
            return [NSString stringWithFormat:@"%@\n%@", jsonStr, @" (⚠️ Invalid json object ⚠️)\n"];
        }
    }
}

@end

then call methods when u need to use them.

ex1. Print NSData for body, response ...etc

NSLog(@"body: %@", [NSString prettifiedJsonStringFromData:[request HTTPBody]]);

ex2. Print NSDictionary

NSLog(@"headers: %@", [NSString prettifiedStringFromDictionary:[request allHTTPHeaderFields]]);

Probably you'll get these results in log.

enter image description here

enter image description here

Bomi Chen
  • 81
  • 4
-1

I can't think of something prettier than a native type.

if let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) {
    if let jsonArray = json as? [Any] { print(jsonArray) }
    else if let jsonDict = json as? [String:Any] { print(jsonDict) }
    else { print("Couldn't convert json") }
}
BallpointBen
  • 9,406
  • 1
  • 32
  • 62
  • I think the goal of the question is to get pretty printed JSON. This answer does not give you JSON. – rmaddy Apr 30 '18 at 23:43
  • If you want to print the representation of something, why does it matter what underlying object gets printed? As long as the output produced faithfully represents the json, it should be fine. – BallpointBen Apr 30 '18 at 23:44
  • @BallpointBen, Your code prints array or dictionary, so it doesn't print the real json which comes server side. – Dmitry Apr 30 '18 at 23:45
  • Seems like a distinction without a difference to me. – BallpointBen Apr 30 '18 at 23:45
  • BTW - Inside the 1st `if let` all you need is `print(json)`. – rmaddy Apr 30 '18 at 23:47
  • 2
    @BallpointBen, I need to see exact data which comes from server side, not modified. – Dmitry Apr 30 '18 at 23:47
  • 1
    @Dmitry one thing I noticed is that decoding, then re-encoding the values via the JSONSerialization class seems to mess with floats. For instance, my original json had `188.71` as a value, but running it through the encoding, then re-decoding yielded `188.71000000000001`. No idea why, but it's pretty frustrating to not get back exactly what went in. Just giving you a heads-up. – Mark A. Donohoe Jul 13 '20 at 18:08