jsonprettyprinter.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. // Copyright 2010 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Creates a string of a JSON object, properly indented for
  16. * display.
  17. *
  18. */
  19. goog.provide('goog.format.JsonPrettyPrinter');
  20. goog.provide('goog.format.JsonPrettyPrinter.SafeHtmlDelimiters');
  21. goog.provide('goog.format.JsonPrettyPrinter.TextDelimiters');
  22. goog.require('goog.html.SafeHtml');
  23. goog.require('goog.json');
  24. goog.require('goog.json.Serializer');
  25. goog.require('goog.string');
  26. goog.require('goog.string.format');
  27. /**
  28. * Formats a JSON object as a string, properly indented for display. Supports
  29. * displaying the string as text or html. Users can also specify their own
  30. * set of delimiters for different environments. For example, the JSON object:
  31. *
  32. * <code>{"a": 1, "b": {"c": null, "d": true, "e": [1, 2]}}</code>
  33. *
  34. * Will be displayed like this:
  35. *
  36. * <code>{
  37. * "a": 1,
  38. * "b": {
  39. * "c": null,
  40. * "d": true,
  41. * "e": [
  42. * 1,
  43. * 2
  44. * ]
  45. * }
  46. * }</code>
  47. * @param {?goog.format.JsonPrettyPrinter.TextDelimiters=} opt_delimiters
  48. * Container for the various strings to use to delimit objects, arrays,
  49. * newlines, and other pieces of the output.
  50. * @constructor
  51. */
  52. goog.format.JsonPrettyPrinter = function(opt_delimiters) {
  53. /**
  54. * The set of characters to use as delimiters.
  55. * @private @const {!goog.format.JsonPrettyPrinter.TextDelimiters}
  56. */
  57. this.delimiters_ =
  58. opt_delimiters || new goog.format.JsonPrettyPrinter.TextDelimiters();
  59. /**
  60. * Used to serialize property names and values.
  61. * @private @const {!goog.json.Serializer}
  62. */
  63. this.jsonSerializer_ = new goog.json.Serializer();
  64. };
  65. /**
  66. * Formats a JSON object as a string, properly indented for display.
  67. * @param {*} json The object to pretty print. It could be a JSON object, a
  68. * string representing a JSON object, or any other type.
  69. * @return {string} Returns a string of the JSON object, properly indented for
  70. * display.
  71. */
  72. goog.format.JsonPrettyPrinter.prototype.format = function(json) {
  73. var buffer = this.format_(json);
  74. var output = '';
  75. for (var i = 0; i < buffer.length; i++) {
  76. var item = buffer[i];
  77. output += item instanceof goog.html.SafeHtml ?
  78. goog.html.SafeHtml.unwrap(item) :
  79. item;
  80. }
  81. return output;
  82. };
  83. /**
  84. * Formats a JSON object as a SafeHtml, properly indented for display.
  85. * @param {*} json The object to pretty print. It could be a JSON object, a
  86. * string representing a JSON object, or any other type.
  87. * @return {!goog.html.SafeHtml} A HTML code of the JSON object.
  88. */
  89. goog.format.JsonPrettyPrinter.prototype.formatSafeHtml = function(json) {
  90. return goog.html.SafeHtml.concat(this.format_(json));
  91. };
  92. /**
  93. * Formats a JSON object and returns an output buffer.
  94. * @param {*} json The object to pretty print.
  95. * @return {!Array<string|!goog.html.SafeHtml>}
  96. * @private
  97. */
  98. goog.format.JsonPrettyPrinter.prototype.format_ = function(json) {
  99. // If input is undefined, null, or empty, return an empty string.
  100. if (!goog.isDefAndNotNull(json)) {
  101. return [];
  102. }
  103. if (goog.isString(json)) {
  104. if (goog.string.isEmptyOrWhitespace(json)) {
  105. return [];
  106. }
  107. // Try to coerce a string into a JSON object.
  108. json = goog.json.parse(json);
  109. }
  110. var outputBuffer = [];
  111. this.printObject_(json, outputBuffer, 0);
  112. return outputBuffer;
  113. };
  114. /**
  115. * Formats a property value based on the type of the propery.
  116. * @param {*} val The object to format.
  117. * @param {!Array<string|!goog.html.SafeHtml>} outputBuffer The buffer to write
  118. * the response to.
  119. * @param {number} indent The number of spaces to indent each line of the
  120. * output.
  121. * @private
  122. */
  123. goog.format.JsonPrettyPrinter.prototype.printObject_ = function(
  124. val, outputBuffer, indent) {
  125. var typeOf = goog.typeOf(val);
  126. switch (typeOf) {
  127. case 'null':
  128. case 'boolean':
  129. case 'number':
  130. case 'string':
  131. // "null", "boolean", "number" and "string" properties are printed
  132. // directly to the output.
  133. this.printValue_(
  134. /** @type {null|string|boolean|number} */ (val), typeOf,
  135. outputBuffer);
  136. break;
  137. case 'array':
  138. // Example of how an array looks when formatted
  139. // (using the default delimiters):
  140. // [
  141. // 1,
  142. // 2,
  143. // 3
  144. // ]
  145. outputBuffer.push(this.delimiters_.arrayStart);
  146. var i = 0;
  147. // Iterate through the array and format each element.
  148. for (i = 0; i < val.length; i++) {
  149. if (i > 0) {
  150. // There are multiple elements, add a comma to separate them.
  151. outputBuffer.push(this.delimiters_.propertySeparator);
  152. }
  153. outputBuffer.push(this.delimiters_.lineBreak);
  154. this.printSpaces_(indent + this.delimiters_.indent, outputBuffer);
  155. this.printObject_(
  156. val[i], outputBuffer, indent + this.delimiters_.indent);
  157. }
  158. // If there are no properties in this object, don't put a line break
  159. // between the beginning "[" and ending "]", so the output of an empty
  160. // array looks like <code>[]</code>.
  161. if (i > 0) {
  162. outputBuffer.push(this.delimiters_.lineBreak);
  163. this.printSpaces_(indent, outputBuffer);
  164. }
  165. outputBuffer.push(this.delimiters_.arrayEnd);
  166. break;
  167. case 'object':
  168. // Example of how an object looks when formatted
  169. // (using the default delimiters):
  170. // {
  171. // "a": 1,
  172. // "b": 2,
  173. // "c": "3"
  174. // }
  175. outputBuffer.push(this.delimiters_.objectStart);
  176. var propertyCount = 0;
  177. // Iterate through the object and display each property.
  178. for (var name in val) {
  179. if (!val.hasOwnProperty(name)) {
  180. continue;
  181. }
  182. if (propertyCount > 0) {
  183. // There are multiple properties, add a comma to separate them.
  184. outputBuffer.push(this.delimiters_.propertySeparator);
  185. }
  186. outputBuffer.push(this.delimiters_.lineBreak);
  187. this.printSpaces_(indent + this.delimiters_.indent, outputBuffer);
  188. this.printName_(name, outputBuffer);
  189. outputBuffer.push(
  190. this.delimiters_.nameValueSeparator, this.delimiters_.space);
  191. this.printObject_(
  192. val[name], outputBuffer, indent + this.delimiters_.indent);
  193. propertyCount++;
  194. }
  195. // If there are no properties in this object, don't put a line break
  196. // between the beginning "{" and ending "}", so the output of an empty
  197. // object looks like <code>{}</code>.
  198. if (propertyCount > 0) {
  199. outputBuffer.push(this.delimiters_.lineBreak);
  200. this.printSpaces_(indent, outputBuffer);
  201. }
  202. outputBuffer.push(this.delimiters_.objectEnd);
  203. break;
  204. // Other types, such as "function", aren't expected in JSON, and their
  205. // behavior is undefined. In these cases, just print an empty string to the
  206. // output buffer. This allows the pretty printer to continue while still
  207. // outputing well-formed JSON.
  208. default:
  209. this.printValue_('', 'unknown', outputBuffer);
  210. }
  211. };
  212. /**
  213. * Prints a property name to the output.
  214. * @param {string} name The property name.
  215. * @param {!Array<string|!goog.html.SafeHtml>} outputBuffer The buffer to write
  216. * the response to.
  217. * @private
  218. */
  219. goog.format.JsonPrettyPrinter.prototype.printName_ = function(
  220. name, outputBuffer) {
  221. outputBuffer.push(
  222. this.delimiters_.formatName(this.jsonSerializer_.serialize(name)));
  223. };
  224. /**
  225. * Prints a property name to the output.
  226. * @param {string|boolean|number|null} val The property value.
  227. * @param {string} typeOf The type of the value. Used to customize
  228. * value-specific css in the display. This allows clients to distinguish
  229. * between different types in css. For example, the client may define two
  230. * classes: "goog-jsonprettyprinter-propertyvalue-string" and
  231. * "goog-jsonprettyprinter-propertyvalue-number" to assign a different color
  232. * to string and number values.
  233. * @param {!Array<string|!goog.html.SafeHtml>} outputBuffer The buffer to write
  234. * the response to.
  235. * @private
  236. */
  237. goog.format.JsonPrettyPrinter.prototype.printValue_ = function(
  238. val, typeOf, outputBuffer) {
  239. var value = this.jsonSerializer_.serialize(val);
  240. outputBuffer.push(this.delimiters_.formatValue(value, typeOf));
  241. };
  242. /**
  243. * Print a number of space characters to the output.
  244. * @param {number} indent The number of spaces to indent the line.
  245. * @param {!Array<string|!goog.html.SafeHtml>} outputBuffer The buffer to write
  246. * the response to.
  247. * @private
  248. */
  249. goog.format.JsonPrettyPrinter.prototype.printSpaces_ = function(
  250. indent, outputBuffer) {
  251. outputBuffer.push(goog.string.repeat(this.delimiters_.space, indent));
  252. };
  253. /**
  254. * A container for the delimiting characters used to display the JSON string
  255. * to a text display. Each delimiter is a publicly accessible property of
  256. * the object, which makes it easy to tweak delimiters to specific environments.
  257. * @constructor
  258. */
  259. goog.format.JsonPrettyPrinter.TextDelimiters = function() {};
  260. /**
  261. * Represents a space character in the output. Used to indent properties a
  262. * certain number of spaces, and to separate property names from property
  263. * values.
  264. * @type {string}
  265. */
  266. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.space = ' ';
  267. /**
  268. * Represents a newline character in the output. Used to begin a new line.
  269. * @type {string|!goog.html.SafeHtml}
  270. */
  271. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.lineBreak = '\n';
  272. /**
  273. * Represents the start of an object in the output.
  274. * @type {string}
  275. */
  276. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.objectStart = '{';
  277. /**
  278. * Represents the end of an object in the output.
  279. * @type {string}
  280. */
  281. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.objectEnd = '}';
  282. /**
  283. * Represents the start of an array in the output.
  284. * @type {string}
  285. */
  286. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.arrayStart = '[';
  287. /**
  288. * Represents the end of an array in the output.
  289. * @type {string}
  290. */
  291. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.arrayEnd = ']';
  292. /**
  293. * Represents the string used to separate properties in the output.
  294. * @type {string}
  295. */
  296. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.propertySeparator = ',';
  297. /**
  298. * Represents the string used to separate property names from property values in
  299. * the output.
  300. * @type {string|!goog.html.SafeHtml}
  301. */
  302. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.nameValueSeparator = ':';
  303. /**
  304. * A string that's placed before a property name in the output. Useful for
  305. * wrapping a property name in an html tag.
  306. * @type {string}
  307. */
  308. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.preName = '';
  309. /**
  310. * A string that's placed after a property name in the output. Useful for
  311. * wrapping a property name in an html tag.
  312. * @type {string}
  313. */
  314. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.postName = '';
  315. /**
  316. * Formats a property name before adding it to the output.
  317. * @param {string} name The property name.
  318. * @return {string|!goog.html.SafeHtml}
  319. */
  320. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.formatName = function(
  321. name) {
  322. return this.preName + name + this.postName;
  323. };
  324. /**
  325. * A string that's placed before a property value in the output. Useful for
  326. * wrapping a property value in an html tag.
  327. * @type {string}
  328. */
  329. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.preValue = '';
  330. /**
  331. * A string that's placed after a property value in the output. Useful for
  332. * wrapping a property value in an html tag.
  333. * @type {string}
  334. */
  335. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.postValue = '';
  336. /**
  337. * Formats a value before adding it to the output.
  338. * @param {string} value The value.
  339. * @param {string} typeOf The type of the value obtained by goog.typeOf.
  340. * @return {string|!goog.html.SafeHtml}
  341. */
  342. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.formatValue = function(
  343. value, typeOf) {
  344. return goog.string.format(this.preValue, typeOf) + value + this.postValue;
  345. };
  346. /**
  347. * Represents the number of spaces to indent each sub-property of the JSON.
  348. * @type {number}
  349. */
  350. goog.format.JsonPrettyPrinter.TextDelimiters.prototype.indent = 2;
  351. /**
  352. * A container for the delimiting characters used to display the JSON string
  353. * to an HTML <code>&lt;pre&gt;</code> or <code>&lt;code&gt;</code> element.
  354. * It escapes the names and values before they are added to the output.
  355. * Use this class together with goog.format.JsonPrettyPrinter#formatSafeHtml.
  356. * @constructor
  357. * @extends {goog.format.JsonPrettyPrinter.TextDelimiters}
  358. */
  359. goog.format.JsonPrettyPrinter.SafeHtmlDelimiters = function() {
  360. goog.format.JsonPrettyPrinter.TextDelimiters.call(this);
  361. };
  362. goog.inherits(
  363. goog.format.JsonPrettyPrinter.SafeHtmlDelimiters,
  364. goog.format.JsonPrettyPrinter.TextDelimiters);
  365. /** @override */
  366. goog.format.JsonPrettyPrinter.SafeHtmlDelimiters.prototype.formatName =
  367. function(name) {
  368. var classes = goog.getCssName('goog-jsonprettyprinter-propertyname');
  369. return goog.html.SafeHtml.create('span', {'class': classes}, name);
  370. };
  371. /** @override */
  372. goog.format.JsonPrettyPrinter.SafeHtmlDelimiters.prototype.formatValue =
  373. function(value, typeOf) {
  374. var classes = this.getValueCssName(typeOf);
  375. return goog.html.SafeHtml.create('span', {'class': classes}, value);
  376. };
  377. /**
  378. * Return a class name for the given type.
  379. * @param {string} typeOf The type of the value.
  380. * @return {string}
  381. * @protected
  382. */
  383. goog.format.JsonPrettyPrinter.SafeHtmlDelimiters.prototype.getValueCssName =
  384. function(typeOf) {
  385. // This switch is needed because goog.getCssName requires a constant string.
  386. switch (typeOf) {
  387. case 'null':
  388. return goog.getCssName('goog-jsonprettyprinter-propertyvalue-null');
  389. case 'boolean':
  390. return goog.getCssName('goog-jsonprettyprinter-propertyvalue-boolean');
  391. case 'number':
  392. return goog.getCssName('goog-jsonprettyprinter-propertyvalue-number');
  393. case 'string':
  394. return goog.getCssName('goog-jsonprettyprinter-propertyvalue-string');
  395. case 'array':
  396. return goog.getCssName('goog-jsonprettyprinter-propertyvalue-array');
  397. case 'object':
  398. return goog.getCssName('goog-jsonprettyprinter-propertyvalue-object');
  399. default:
  400. return goog.getCssName('goog-jsonprettyprinter-propertyvalue-unknown');
  401. }
  402. };