/// <summary> /// Creates a string representation of a JsonObj, list, string, or double that conforms to the JSON or JSON5 standards. /// </summary> /// <param name="object">The object to stringify.</param> /// <param name="space">How many spaces to indent each level.</param> /// <returns>A string representation of the input object.</returns> public static string Stringify(this object @object, int space = 2) { var previousCulture = System.Threading.Thread.CurrentThread.CurrentCulture; System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; Func <char, bool> isWordChar = (c) => { return((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '$'); }; Func <char, bool> isWordStart = (c) => { return((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$'); }; Func <object, bool> isWord = (key) => { if (!AllowBareKeys) { return(false); } if (!(key is string)) { return(false); } var k = key as string; if (!isWordStart(k[0])) { return(false); } var i = 1; while (i < k.Length) { if (!isWordChar(k[i])) { return(false); } i++; } return(true); }; var objStack = new Stack <object>(); Action <object> checkForCircular = (obj) => { if (objStack.Contains(obj)) { throw new JsonException("Converting circular structure to JSON"); } }; Func <string, int, bool, string> makeIndent = (str, num, noNewLine) => { if (string.IsNullOrEmpty(str)) { return(string.Empty); } // indentation no more than 10 chars if (str.Length > 10) { str = str.Substring(0, 10); } var indent = new StringBuilder(noNewLine ? "" : "\n"); for (var i = 0; i < num; i++) { indent.Append(str); } return(indent.ToString()); }; var indentStr = space == 0 ? string.Empty : makeIndent(" ", space, true); Func <string, string> escapeString = (str) => { return('\"' + str.Replace("\"", "\\\"") + '\"'); }; Func <object, string> internalStringify = null; internalStringify = (obj_part) => { var buffer = new StringBuilder(); var singleBuffer = new StringBuilder(); var res = string.Empty; if (obj_part == null) { return("null"); } if (obj_part is bool) { return(obj_part.ToString().ToLowerInvariant()); } if (obj_part is double || obj_part is float) { if (!AllowNaN && (double.IsNaN((double)obj_part) || double.IsInfinity((double)obj_part))) { throw new JsonException("Found an unallowed NaN or Infinity value."); } else { return(obj_part.ToString()); } } if (obj_part is int || obj_part is long || obj_part is byte || obj_part is sbyte) { return(obj_part.ToString()); } if (obj_part is int[]) { return(internalStringify(((int[])obj_part).Cast <object>().ToList())); } if (obj_part is long[]) { return(internalStringify(((long[])obj_part).Cast <object>().ToList())); } if (obj_part is byte[]) { return(internalStringify(((byte[])obj_part).Cast <object>().ToList())); } if (obj_part is sbyte[]) { return(internalStringify(((sbyte[])obj_part).Cast <object>().ToList())); } if (obj_part is double[]) { return(internalStringify(((double[])obj_part).Cast <object>().ToList())); } if (obj_part is float[]) { return(internalStringify(((float[])obj_part).Cast <object>().ToList())); } if (obj_part is string[]) { return(internalStringify(((string[])obj_part).Cast <object>().ToList())); } if (obj_part is List <int> ) { return(internalStringify(((List <int>)obj_part).Cast <object>().ToList())); } if (obj_part is List <long> ) { return(internalStringify(((List <long>)obj_part).Cast <object>().ToList())); } if (obj_part is List <byte> ) { return(internalStringify(((List <byte>)obj_part).Cast <object>().ToList())); } if (obj_part is List <sbyte> ) { return(internalStringify(((List <sbyte>)obj_part).Cast <object>().ToList())); } if (obj_part is List <double> ) { return(internalStringify(((List <double>)obj_part).Cast <object>().ToList())); } if (obj_part is List <float> ) { return(internalStringify(((List <float>)obj_part).Cast <object>().ToList())); } if (obj_part is List <string> ) { return(internalStringify(((List <string>)obj_part).Cast <object>().ToList())); } if (obj_part is string) { return(escapeString(obj_part.ToString())); } if (obj_part is object) { if (obj_part is object[]) { obj_part = ((object[])obj_part).ToList(); } if (obj_part == null) { return("null"); } else if (obj_part is List <object> ) { checkForCircular(obj_part); var objPartAsArray = obj_part as List <object>; if (objPartAsArray.Count == 0) { return("[]"); } buffer.Append('['); singleBuffer.Append('['); objStack.Push(obj_part); for (var i = 0; i < objPartAsArray.Count; i++) { res = internalStringify(objPartAsArray[i]); buffer.Append(makeIndent(indentStr, objStack.Count, false)); singleBuffer.Append(' '); if (res == null) { buffer.Append("null"); singleBuffer.Append("null"); } else { buffer.Append(res); singleBuffer.Append(res); } if (i < objPartAsArray.Count - 1) { buffer.Append(','); singleBuffer.Append(','); } else if (string.IsNullOrEmpty(indentStr)) { buffer.Append('\n'); } } objStack.Pop(); buffer.Append(makeIndent(indentStr, objStack.Count, false)); buffer.Append(']'); singleBuffer.Append(" ]"); if (FoldMode.HasFlag(FoldMode.Arrays) && singleBuffer.Length < FoldLength) { return(singleBuffer.ToString()); } } else if (obj_part is JsonObj) { checkForCircular(obj_part); buffer.Append('{'); singleBuffer.Append('{'); objStack.Push(obj_part); var nonEmpty = false; var objPartAsDict = obj_part as JsonObj; foreach (var pair in objPartAsDict) { var val = internalStringify(pair.Value); if (val != null) { buffer.Append(makeIndent(indentStr, objStack.Count, false)); singleBuffer.Append(' '); nonEmpty = true; var key = isWord(pair.Key) ? pair.Key : escapeString(pair.Key); buffer.AppendFormat("{0} : {1},", key, val); singleBuffer.AppendFormat("{0} : {1},", key, val); } } objStack.Pop(); if (nonEmpty) { if (FoldMode.HasFlag(FoldMode.Objects) && singleBuffer.Length < FoldLength) { return(singleBuffer.ToString().Substring(0, singleBuffer.Length - 1) + " }"); } return(buffer.ToString().Substring(0, buffer.Length - 1) + makeIndent(indentStr, objStack.Count, false) + '}'); } return("{}"); } return(buffer.ToString()); } else { return(null); } }; var ret = internalStringify(@object); System.Threading.Thread.CurrentThread.CurrentCulture = previousCulture; return(ret); }