1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5var EXPORTED_SYMBOLS = ["CanonicalJSON"]; 6 7var CanonicalJSON = { 8 /** 9 * Return the canonical JSON form of the passed source, sorting all the object 10 * keys recursively. Note that this method will cause an infinite loop if 11 * cycles exist in the source (bug 1265357). 12 * 13 * @param source 14 * The elements to be serialized. 15 * 16 * The output will have all unicode chars escaped with the unicode codepoint 17 * as lowercase hexadecimal. 18 * 19 * @usage 20 * CanonicalJSON.stringify(listOfRecords); 21 **/ 22 stringify: function stringify(source, jsescFn) { 23 if (typeof jsescFn != "function") { 24 const { jsesc } = ChromeUtils.import( 25 "resource://gre/modules/third_party/jsesc/jsesc.js" 26 ); 27 jsescFn = jsesc; 28 } 29 if (Array.isArray(source)) { 30 const jsonArray = source.map(x => (typeof x === "undefined" ? null : x)); 31 return ( 32 "[" + jsonArray.map(item => stringify(item, jsescFn)).join(",") + "]" 33 ); 34 } 35 36 if (typeof source === "number") { 37 if (source === 0) { 38 return Object.is(source, -0) ? "-0" : "0"; 39 } 40 } 41 42 // Leverage jsesc library, mainly for unicode escaping. 43 const toJSON = input => jsescFn(input, { lowercaseHex: true, json: true }); 44 45 if (typeof source !== "object" || source === null) { 46 return toJSON(source); 47 } 48 49 // Dealing with objects, ordering keys. 50 const sortedKeys = Object.keys(source).sort(); 51 const lastIndex = sortedKeys.length - 1; 52 return ( 53 sortedKeys.reduce((serial, key, index) => { 54 const value = source[key]; 55 // JSON.stringify drops keys with an undefined value. 56 if (typeof value === "undefined") { 57 return serial; 58 } 59 const jsonValue = value && value.toJSON ? value.toJSON() : value; 60 const suffix = index !== lastIndex ? "," : ""; 61 const escapedKey = toJSON(key); 62 return ( 63 serial + escapedKey + ":" + stringify(jsonValue, jsescFn) + suffix 64 ); 65 }, "{") + "}" 66 ); 67 }, 68}; 69