// Copyright 2010 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Creates a string of a JSON object, properly indented for * display. * */ goog.provide('goog.format.JsonPrettyPrinter'); goog.provide('goog.format.JsonPrettyPrinter.HtmlDelimiters'); goog.provide('goog.format.JsonPrettyPrinter.TextDelimiters'); goog.require('goog.json'); goog.require('goog.json.Serializer'); goog.require('goog.string'); goog.require('goog.string.StringBuffer'); goog.require('goog.string.format'); /** * Formats a JSON object as a string, properly indented for display. Supports * displaying the string as text or html. Users can also specify their own * set of delimiters for different environments. For example, the JSON object: * * {"a": 1, "b": {"c": null, "d": true, "e": [1, 2]}} * * Will be displayed like this: * * { * "a": 1, * "b": { * "c": null, * "d": true, * "e": [ * 1, * 2 * ] * } * } * @param {goog.format.JsonPrettyPrinter.TextDelimiters} delimiters Container * for the various strings to use to delimit objects, arrays, newlines, and * other pieces of the output. * @constructor */ goog.format.JsonPrettyPrinter = function(delimiters) { /** * The set of characters to use as delimiters. * @type {goog.format.JsonPrettyPrinter.TextDelimiters} * @private */ this.delimiters_ = delimiters || new goog.format.JsonPrettyPrinter.TextDelimiters(); /** * Used to serialize property names and values. * @type {goog.json.Serializer} * @private */ this.jsonSerializer_ = new goog.json.Serializer(); }; /** * Formats a JSON object as a string, properly indented for display. * @param {*} json The object to pretty print. It could be a JSON object, a * string representing a JSON object, or any other type. * @return {string} Returns a string of the JSON object, properly indented for * display. */ goog.format.JsonPrettyPrinter.prototype.format = function(json) { // If input is undefined, null, or empty, return an empty string. if (!goog.isDefAndNotNull(json)) { return ''; } if (goog.isString(json)) { if (goog.string.isEmpty(json)) { return ''; } // Try to coerce a string into a JSON object. json = goog.json.parse(json); } var outputBuffer = new goog.string.StringBuffer(); this.printObject_(json, outputBuffer, 0); return outputBuffer.toString(); }; /** * Formats a property value based on the type of the propery. * @param {*} val The object to format. * @param {goog.string.StringBuffer} outputBuffer The buffer to write the * response to. * @param {number} indent The number of spaces to indent each line of the * output. * @private */ goog.format.JsonPrettyPrinter.prototype.printObject_ = function(val, outputBuffer, indent) { var typeOf = goog.typeOf(val); switch (typeOf) { case 'null': case 'boolean': case 'number': case 'string': // "null", "boolean", "number" and "string" properties are printed // directly to the output. this.printValue_( /** @type {null|string|boolean|number} */ (val), typeOf, outputBuffer); break; case 'array': // Example of how an array looks when formatted // (using the default delimiters): // [ // 1, // 2, // 3 // ] outputBuffer.append(this.delimiters_.arrayStart); var i = 0; // Iterate through the array and format each element. for (i = 0; i < val.length; i++) { if (i > 0) { // There are multiple elements, add a comma to separate them. outputBuffer.append(this.delimiters_.propertySeparator); } outputBuffer.append(this.delimiters_.lineBreak); this.printSpaces_(indent + this.delimiters_.indent, outputBuffer); this.printObject_(val[i], outputBuffer, indent + this.delimiters_.indent); } // If there are no properties in this object, don't put a line break // between the beginning "[" and ending "]", so the output of an empty // array looks like []. if (i > 0) { outputBuffer.append(this.delimiters_.lineBreak); this.printSpaces_(indent, outputBuffer); } outputBuffer.append(this.delimiters_.arrayEnd); break; case 'object': // Example of how an object looks when formatted // (using the default delimiters): // { // "a": 1, // "b": 2, // "c": "3" // } outputBuffer.append(this.delimiters_.objectStart); var propertyCount = 0; // Iterate through the object and display each property. for (var name in val) { if (!val.hasOwnProperty(name)) { continue; } if (propertyCount > 0) { // There are multiple properties, add a comma to separate them. outputBuffer.append(this.delimiters_.propertySeparator); } outputBuffer.append(this.delimiters_.lineBreak); this.printSpaces_(indent + this.delimiters_.indent, outputBuffer); this.printName_(name, outputBuffer); outputBuffer.append(this.delimiters_.nameValueSeparator, this.delimiters_.space); this.printObject_(val[name], outputBuffer, indent + this.delimiters_.indent); propertyCount++; } // If there are no properties in this object, don't put a line break // between the beginning "{" and ending "}", so the output of an empty // object looks like {}. if (propertyCount > 0) { outputBuffer.append(this.delimiters_.lineBreak); this.printSpaces_(indent, outputBuffer); } outputBuffer.append(this.delimiters_.objectEnd); break; // Other types, such as "function", aren't expected in JSON, and their // behavior is undefined. In these cases, just print an empty string to the // output buffer. This allows the pretty printer to continue while still // outputing well-formed JSON. default: this.printValue_('', 'unknown', outputBuffer); } }; /** * Prints a property name to the output. * @param {string} name The property name. * @param {goog.string.StringBuffer} outputBuffer The buffer to write the * response to. * @private */ goog.format.JsonPrettyPrinter.prototype.printName_ = function(name, outputBuffer) { outputBuffer.append(this.delimiters_.preName, this.jsonSerializer_.serialize(name), this.delimiters_.postName); }; /** * Prints a property name to the output. * @param {string|boolean|number|null} val The property value. * @param {string} typeOf The type of the value. Used to customize * value-specific css in the display. This allows clients to distinguish * between different types in css. For example, the client may define two * classes: "goog-jsonprettyprinter-propertyvalue-string" and * "goog-jsonprettyprinter-propertyvalue-number" to assign a different color * to string and number values. * @param {goog.string.StringBuffer} outputBuffer The buffer to write the * response to. * @private */ goog.format.JsonPrettyPrinter.prototype.printValue_ = function(val, typeOf, outputBuffer) { outputBuffer.append(goog.string.format(this.delimiters_.preValue, typeOf), this.jsonSerializer_.serialize(val), goog.string.format(this.delimiters_.postValue, typeOf)); }; /** * Print a number of space characters to the output. * @param {number} indent The number of spaces to indent the line. * @param {goog.string.StringBuffer} outputBuffer The buffer to write the * response to. * @private */ goog.format.JsonPrettyPrinter.prototype.printSpaces_ = function(indent, outputBuffer) { outputBuffer.append(goog.string.repeat(this.delimiters_.space, indent)); }; /** * A container for the delimiting characters used to display the JSON string * to a text display. Each delimiter is a publicly accessible property of * the object, which makes it easy to tweak delimiters to specific environments. * @constructor */ goog.format.JsonPrettyPrinter.TextDelimiters = function() { }; /** * Represents a space character in the output. Used to indent properties a * certain number of spaces, and to separate property names from property * values. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.space = ' '; /** * Represents a newline character in the output. Used to begin a new line. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.lineBreak = '\n'; /** * Represents the start of an object in the output. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.objectStart = '{'; /** * Represents the end of an object in the output. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.objectEnd = '}'; /** * Represents the start of an array in the output. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.arrayStart = '['; /** * Represents the end of an array in the output. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.arrayEnd = ']'; /** * Represents the string used to separate properties in the output. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.propertySeparator = ','; /** * Represents the string used to separate property names from property values in * the output. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.nameValueSeparator = ':'; /** * A string that's placed before a property name in the output. Useful for * wrapping a property name in an html tag. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.preName = ''; /** * A string that's placed after a property name in the output. Useful for * wrapping a property name in an html tag. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.postName = ''; /** * A string that's placed before a property value in the output. Useful for * wrapping a property value in an html tag. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.preValue = ''; /** * A string that's placed after a property value in the output. Useful for * wrapping a property value in an html tag. * @type {string} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.postValue = ''; /** * Represents the number of spaces to indent each sub-property of the JSON. * @type {number} */ goog.format.JsonPrettyPrinter.TextDelimiters.prototype.indent = 2; /** * A container for the delimiting characters used to display the JSON string * to an HTML <pre> or <code> element. * @constructor * @extends {goog.format.JsonPrettyPrinter.TextDelimiters} */ goog.format.JsonPrettyPrinter.HtmlDelimiters = function() { goog.format.JsonPrettyPrinter.TextDelimiters.call(this); }; goog.inherits(goog.format.JsonPrettyPrinter.HtmlDelimiters, goog.format.JsonPrettyPrinter.TextDelimiters); /** * A span tag thats placed before a property name. Used to style * property names with CSS. * @type {string} * @override */ goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.preName = ''; /** * A closing span tag that's placed after a property name. * @type {string} * @override */ goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.postName = ''; /** * A span tag thats placed before a property value. Used to style * property value with CSS. The span tag's class is in the format * goog-jsonprettyprinter-propertyvalue-{TYPE}, where {TYPE} is the JavaScript * type of the object (the {TYPE} parameter is obtained from goog.typeOf). This * can be used to style different value types. * @type {string} * @override */ goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.preValue = ''; /** * A closing span tag that's placed after a property value. * @type {string} * @override */ goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.postValue = '';