Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Python

JSON Pretty Print and JSON Multi level Collapse Code in JavaScript and Python

5.00/5 (1 vote)
23 Jul 2012CPOL2 min read 18.4K  
If you are the type of programmer that deals with JSON objects a lot, you are probably familiar with some online programming tools to print out the JSON in a very “pretty” format. These tools not only make dealing with JSON easier and … Continue reading →

If you are the type of programmer that deals with JSON objects a lot, you are probably familiar with some online programming tools to print out the JSON in a very "pretty" format. These tools not only make dealing with JSON easier and less frustrating but, I must admit, make it more fun too!

A "JSON viewer" app that I use regularly to "pretty-print" complicated JSON objects is at jsonviewer.stack.hu. The app has a very intuitive interface and is thus really easy to use. You can just copy a JSON object to the front page of the app or pull a JSON object from a remote location and then "format" the json. There are many more helpful JSON viewer apps and extensions that are helpful for viewing JSON.

Have you ever had the problem of trying to "pretty-print" your JSON object in your program or app for users to see, or to "collapse" your multi-level JSON object (which might contain arrays in objects and objects of arrays and …) programmatically into one level? I have certainly. Programmers that usually have these problems regularly use or make RESTFUL API calls — although RESTful "resources" can be transmitted in XML, SOAP, or even YAML, the predominant format on the web is JSON.

"JSON multi-level Collapse" Code in JavaScript and Python

I cooked up the "multi-level collapse JSON" algorithm three days ago because I needed to, given a bunch of streaming JSON objects, collapse each JSON object into the most minimal form and then assemble the JSON objects into a CSV file where each JSON object occupies only one line in the CSV. Yes, my program had to first collapse any JSON object (might contain embedded objects or arrays of objects of arrays of objects of…) into a one-level JSON object without losing any information.

"Multi-level collapse" code in JavaScript

JavaScript
// CollapseLib by Daniel Alabi (alabidan.me)
// isPlainObject is part of the jQuery source (I modified the method a little)

/**
 * Example use: 
 * complicated = {"dict1key": {"dict2key": [{"dict3key": {"tell":"me"}}]}}
 *
 * var dict = CollapseLib.collapseDict(collapse);
 * console.log(JSON.stringify(dict, undefined, 2));
 *
 */

var CollapseLib = {
    /**
     * 
     * @warning allowed types for complexDict : {}, [], JS primitives (int, float, or string)
     *
     * @param complexDict : a JS object that might have inner JS objects and arrays OR
     *                      a JS array that might have inner JS objects and arrays OR
     * @param plainDict   : a one-level collapse JS object
     */
    collapseDict : function(complexDict) {
	// make plain object to return
	var plainDict = {}
	, sawComplex = false
	, subDict;

	if (CollapseLib.isPlainObject(complexDict)) {
	    // if complexDict is a JS object
	    sawComplex = false;
	    for (var complexKey in complexDict) {
		// if complexDict[complexKey] is an inner dict
		if (CollapseLib.isPlainObject(complexDict[complexKey])) {
		    subDict = complexDict[complexKey];
		    sawComplex = true;
		    for (var subKey in subDict) {
			plainDict[complexKey+"."+subKey] = CollapseLib.collapseDict(subDict[subKey]);
		    }
		} else if (Array.isArray && Array.isArray(complexDict[complexKey])) {
		    if (!CollapseLib.isComplexArray(complexDict[complexKey])) {
			plainDict[complexKey] = CollapseLib.getStrFromArray(complexDict[complexKey]);
		    } else {
			sawComplex = true;
			for (var i = 0; i < complexDict[complexKey].length; i++) {
			    plainDict[complexKey + "[" + i + "]"] = CollapseLib.collapseDict(complexDict[complexKey][i]);
			}
		    }
		} else {
		    plainDict[complexKey] = CollapseLib.collapseDict(complexDict[complexKey]);
		}	
	    }
	    if (sawComplex) {
		return CollapseLib.collapseDict(plainDict);
	    } else {
		return plainDict;
	    }
	} else {
	    if (Array.isArray && Array.isArray(complexDict)) {
		plainDict = {};
		
		// if complexDict is an array
		// that contains an inner array or inner plain object
		if (CollapseLib.isComplexArray(complexDict)) {
		    for (var i = 0; i < complexDict.length; i++) {
			plainDict["["+i+"]"] = CollapseLib.collapseDict(complexDict[i]);
		    }
		}
		return CollapseLib.collapseDict(plainDict);
	    } else {
		return complexDict.toString();
	    }
	}
    }
    
    , isComplexArray : function(arr) {
	for (var i = 0; i < arr.length; i++) {
	    if ((Array.isArray && Array.isArray(arr[i]))
		|| CollapseLib.isPlainObject(arr[i])) {
		return true;
	    }
	}
	return false;
    }
    
    , getStrFromArray : function(arr) {
	return arr.toString();
    }
    
    , isPlainObject: function( obj ) {
	var hasOwn = Object.prototype.hasOwnProperty;
	// Must be an Object.
	// Because of IE, we also have to check the presence of the constructor property.
	// Make sure that DOM nodes and window objects don't pass through, as well
	if ( !obj || typeof obj !== "object" || obj.nodeType) {
            return false;
	}
	
	try {
            // Not own constructor property must be Object
            if ( obj.constructor &&
		 !hasOwn.call(obj, "constructor") &&
		 !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
		return false;
            }
	} catch ( e ) {
            // IE8,9 Will throw exceptions on certain host objects #9897
            return false;
	}
	
	// Own properties are enumerated firstly, so to speed up,
	// if last one is own, then all properties are own.
	var key;
	for ( key in obj ) {}
	
	return key === undefined || hasOwn.call( obj, key );
    }
}

"Multi-level collapse" code in Python

Python
 """
# How to use:

complicated_dict = {"dict1key": {"dict2key": [{"dict3key": {"tell":"me"}}]}}
one_level_dict = makeSimpleDict(complicated_dict)
print one_level_dict
# prints out {'dict1key.dict2key.[0].dict3key.tell': u'me'}
"""
def makeSimpleDict(complex_dict):
    """
    @param complex_dict : a python dict that might have inner dicts and arrays OR
    a python list that might have inner dicts and arrays OR
    a python object that's neither list of dict
    
    @return plain_dict : plain dict that has only one level or a plain python object
    """
    # make plain dict that you will return
    plain_dict = {} 

    if isinstance(complex_dict, dict):
        # if complex_dict is a dict
        # loop through the keys of this complex dict
        sawComplex = False
        for complex_key in complex_dict:
            # if complex_dict[complex_key] is a dict
            if isinstance(complex_dict[complex_key], dict):
                sub_dict = complex_dict[complex_key]
                # loop through the keys of this sub_dict
                sawComplex = True
                for sub_key in sub_dict:
                    plain_dict[complex_key+"."+sub_key] = makeSimpleDict(sub_dict[sub_key])
            elif isinstance(complex_dict[complex_key], list):
                if not isComplexList(complex_dict[complex_key]):
                    plain_dict[complex_key] = getStrFromList(complex_dict[complex_key])
                else: 
                    sawComplex = True
                    for i in range(len(complex_dict[complex_key])):
                        plain_dict[complex_key+"["+str(i)+"]"] = makeSimpleDict(complex_dict[complex_key][i])
            else:
                plain_dict[complex_key] = makeSimpleDict(complex_dict[complex_key])
        if sawComplex:
            return makeSimpleDict(plain_dict)
        else:
            return plain_dict
    else:
        # if not a dict
        if isinstance(complex_dict, list):
            # if complex_dict is a list
            
            # is complex_dict a list
            # that contains a dict or an inner list?
            if not isComplexList(complex_dict):
                accum = getStrFromList(complex_dict)
                plain_dict = accum
            else:
                # loop through the complex_dict
                for i in range(len(complex_dict)):
                    plain_dict["["+str(i)+"]"] = makeSimpleDict(complex_dict[i])
            return makeSimpleDict(plain_dict)                        
        else:
            # if neither a list nor a dict
            return unicode(complex_dict)
                    
                    
def isComplexList(ls):
    for each in ls:
        if isinstance(each, dict) or isinstance(each, list):
            return True
    return False
    
def getStrFromList(ls):
    if isinstance(ls, list):
        return ", ".join([unicode(each) for each in ls])
    else:
        return ls

For example, the output of the "multi-level" algorithm on input the JSON object

{"apiVersion":"2.0","data":{"updated":"2010-01-07T19:58:42.949Z","totalItems":800,"startIndex":1,
  "itemsPerPage":1,"items":[{"id":"hYB0mn5zh2c","uploaded":"2007-06-05T22:07:03.000Z",
  "updated":"2010-01-07T13:26:50.000Z","uploader":"GoogleDeveloperDay","category":"News",
  "title":"Google Developers Day US - Maps API Introduction","description":"Google Maps API Introduction ...",
  "tags":["GDD07","GDD07US","Maps"],"thumbnail":{"default":"http://i.ytimg.com/vi/hYB0mn5zh2c/default.jpg",
  "hqDefault":"http://i.ytimg.com/vi/hYB0mn5zh2c/hqdefault.jpg"},
  "player":{"default":"https://www.youtube.com/watch?v=hYB0mn5zh2c","mobile":"https://m.youtube.com/details?v=hYB0mn5zh2c"},
  "content":{"1":"rtsp://v5.cache3.c.youtube.com/CiILENy.../0/0/0/video.3gp",
  "5":"http://www.youtube.com/v/hYB0mn5zh2c?f...","6":"rtsp://v1.cache1.c.youtube.com/CiILENy.../0/0/0/video.3gp"},
  "duration":2840,"aspectRatio":"widescreen","likeCount":171,"rating":4.63,"ratingCount":68,"viewCount":220101,
  "favoriteCount":201,"commentCount":22,"status":{"value":"restricted","reason":"limitedSyndication"},
  "accessControl":{"syndicate":"allowed","commentVote":"allowed","rate":"allowed","list":"allowed",
  "comment":"allowed","embed":"allowed","videoRespond":"moderated"}}]}}

is

JavaScript
{
  "apiVersion": "2.0",
  "data.updated": "2010-01-07T19:58:42.949Z",
  "data.totalItems": "800",
  "data.startIndex": "1",
  "data.itemsPerPage": "1",
  "data.items.[0].id": "hYB0mn5zh2c",
  "data.items.[0].uploaded": "2007-06-05T22:07:03.000Z",
  "data.items.[0].updated": "2010-01-07T13:26:50.000Z",
  "data.items.[0].uploader": "GoogleDeveloperDay",
  "data.items.[0].category": "News",
  "data.items.[0].title": "Google Developers Day US - Maps API Introduction",
  "data.items.[0].description": "Google Maps API Introduction ...",
  "data.items.[0].tags": "GDD07,GDD07US,Maps",
  "data.items.[0].thumbnail.default": "http://i.ytimg.com/vi/hYB0mn5zh2c/default.jpg",
  "data.items.[0].thumbnail.hqDefault": "http://i.ytimg.com/vi/hYB0mn5zh2c/hqdefault.jpg",
  "data.items.[0].player.default": "https://www.youtube.com/watch?v=hYB0mn5zh2c",
  "data.items.[0].player.mobile": "https://m.youtube.com/details?v=hYB0mn5zh2c",
  "data.items.[0].content.1": "rtsp://v5.cache3.c.youtube.com/CiILENy.../0/0/0/video.3gp",
  "data.items.[0].content.5": "http://www.youtube.com/v/hYB0mn5zh2c?f...",
  "data.items.[0].content.6": "rtsp://v1.cache1.c.youtube.com/CiILENy.../0/0/0/video.3gp",
  "data.items.[0].duration": "2840",
  "data.items.[0].aspectRatio": "widescreen",
  "data.items.[0].likeCount": "171",
  "data.items.[0].rating": "4.63",
  "data.items.[0].ratingCount": "68",
  "data.items.[0].viewCount": "220101",
  "data.items.[0].favoriteCount": "201",
  "data.items.[0].commentCount": "22",
  "data.items.[0].status.value": "restricted",
  "data.items.[0].status.reason": "limitedSyndication",
  "data.items.[0].accessControl.syndicate": "allowed",
  "data.items.[0].accessControl.commentVote": "allowed",
  "data.items.[0].accessControl.rate": "allowed",
  "data.items.[0].accessControl.list": "allowed",
  "data.items.[0].accessControl.comment": "allowed",
  "data.items.[0].accessControl.embed": "allowed",
  "data.items.[0].accessControl.videoRespond": "moderated"
}

"Pretty Print" Code in JavaScript

The goal of the "Pretty Print" code is to, given a JSON object (usually used for complicated JSON objects that’s multi-level and really nested), print the JSON object in a very clear way so that the user sees the embedded arrays, embedded JSON objects, and embedded arrays in objects and vice versa present in the initial parent object.

So for example, the output of the "pretty print" algorithm on input the JSON object below:

JavaScript
{"apiVersion":"2.0","data":{"updated":"2010-01-07T19:58:42.949Z",
  "totalItems":800,"startIndex":1,"itemsPerPage":1,"items":[{"id":"hYB0mn5zh2c",
  "uploaded":"2007-06-05T22:07:03.000Z","updated":"2010-01-07T13:26:50.000Z",
  "uploader":"GoogleDeveloperDay","category":"News","title":
  "Google Developers Day US - Maps API Introduction","description":
  "Google Maps API Introduction ...","tags":["GDD07","GDD07US","Maps"],
  "thumbnail":{"default":"http://i.ytimg.com/vi/hYB0mn5zh2c/default.jpg",
  "hqDefault":"http://i.ytimg.com/vi/hYB0mn5zh2c/hqdefault.jpg"},"player":
  {"default":"https://www.youtube.com/watch?v=hYB0mn5zh2c","mobile":
  "https://m.youtube.com/details?v=hYB0mn5zh2c"},
  "content":{"1":"rtsp://v5.cache3.c.youtube.com/CiILENy.../0/0/0/video.3gp",
  "5":"http://www.youtube.com/v/hYB0mn5zh2c?f...","6":
  "rtsp://v1.cache1.c.youtube.com/CiILENy.../0/0/0/video.3gp"},
  "duration":2840,"aspectRatio":"widescreen","likeCount":171,"rating":4.63,
  "ratingCount":68,"viewCount":220101,"favoriteCount":201,"commentCount":22,
  "status":{"value":"restricted","reason":"limitedSyndication"},
  "accessControl":{"syndicate":"allowed","commentVote":"allowed",
  "rate":"allowed","list":"allowed","comment":"allowed",
  "embed":"allowed","videoRespond":"moderated"}}]}}

is

JavaScript
{
  "apiVersion": "2.0",
  "data": {
    "updated": "2010-01-07T19:58:42.949Z",
    "totalItems": 800,
    "startIndex": 1,
    "itemsPerPage": 1,
    "items": [
      {
        "id": "hYB0mn5zh2c",
        "uploaded": "2007-06-05T22:07:03.000Z",
        "updated": "2010-01-07T13:26:50.000Z",
        "uploader": "GoogleDeveloperDay",
        "category": "News",
        "title": "Google Developers Day US - Maps API Introduction",
        "description": "Google Maps API Introduction ...",
        "tags": [
          "GDD07",
          "GDD07US",
          "Maps"
        ],
        "thumbnail": {
          "default": "http://i.ytimg.com/vi/hYB0mn5zh2c/default.jpg",
          "hqDefault": "http://i.ytimg.com/vi/hYB0mn5zh2c/hqdefault.jpg"
        },
        "player": {
          "default": "https://www.youtube.com/watch?v=hYB0mn5zh2c",
          "mobile": "https://m.youtube.com/details?v=hYB0mn5zh2c"
        },
        "content": {
          "1": "rtsp://v5.cache3.c.youtube.com/CiILENy.../0/0/0/video.3gp",
          "5": "http://www.youtube.com/v/hYB0mn5zh2c?f...",
          "6": "rtsp://v1.cache1.c.youtube.com/CiILENy.../0/0/0/video.3gp"
        },
        "duration": 2840,
        "aspectRatio": "widescreen",
        "likeCount": 171,
        "rating": 4.63,
        "ratingCount": 68,
        "viewCount": 220101,
        "favoriteCount": 201,
        "commentCount": 22,
        "status": {
          "value": "restricted",
          "reason": "limitedSyndication"
        },
        "accessControl": {
          "syndicate": "allowed",
          "commentVote": "allowed",
          "rate": "allowed",
          "list": "allowed",
          "comment": "allowed",
          "embed": "allowed",
          "videoRespond": "moderated"
        }
      }
    ]
  }
}

The "pretty print" code in Javascript

It’s just a simple line of JS but not many people know about how to pretty print using an already existing function in JS.

JavaScript
JSON.stringify(objectToPrettyPrint, undefined, 2);

For more information, check out the MDN Docs.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)