Formateador JSON en C #?

Buscando una función que tomará una string de Json como entrada y la formateará con saltos de línea y sangrías. La validación sería una ventaja, pero no es necesaria, y no es necesario analizarla en un objeto ni nada.

Alguien sabe de una biblioteca así?


Muestra de entrada:

 {"status":"OK", "results":[ {"types":[ "locality", "political"], "formatted_address":"New York, NY, USA", "address_components":[ {"long_name":"New York", "short_name":"New York", "types":[ "locality", "political"]}, {"long_name":"New York", "short_name":"New York", "types":[ "administrative_area_level_2", "political"]}, {"long_name":"New York", "short_name":"NY", "types":[ "administrative_area_level_1", "political"]}, {"long_name":"United States", "short_name":"US", "types":[ "country", "political"]}], "geometry":{"location":{"lat":40.7143528, "lng":-74.0059731}, "location_type":"APPROXIMATE", "viewport":{"southwest":{"lat":40.5788964, "lng":-74.2620919}, "northeast":{"lat":40.8495342, "lng":-73.7498543}}, "bounds":{"southwest":{"lat":40.4773990, "lng":-74.2590900}, "northeast":{"lat":40.9175770, "lng":-73.7002720}}}}]} 

Actualicé la versión anterior, ahora debe admitir valores sin comillas como enteros y booleanos.

Refactoré la versión anterior y obtuve la versión final: el código es más corto y más limpio. Solo requiere un método de extensión. Lo más importante: corrigió algunos errores.

 class JsonHelper { private const string INDENT_STRING = " "; public static string FormatJson(string str) { var indent = 0; var quoted = false; var sb = new StringBuilder(); for (var i = 0; i < str.Length; i++) { var ch = str[i]; switch (ch) { case '{': case '[': sb.Append(ch); if (!quoted) { sb.AppendLine(); Enumerable.Range(0, ++indent).ForEach(item => sb.Append(INDENT_STRING)); } break; case '}': case ']': if (!quoted) { sb.AppendLine(); Enumerable.Range(0, --indent).ForEach(item => sb.Append(INDENT_STRING)); } sb.Append(ch); break; case '"': sb.Append(ch); bool escaped = false; var index = i; while (index > 0 && str[--index] == '\\') escaped = !escaped; if (!escaped) quoted = !quoted; break; case ',': sb.Append(ch); if (!quoted) { sb.AppendLine(); Enumerable.Range(0, indent).ForEach(item => sb.Append(INDENT_STRING)); } break; case ':': sb.Append(ch); if (!quoted) sb.Append(" "); break; default: sb.Append(ch); break; } } return sb.ToString(); } } static class Extensions { public static void ForEach(this IEnumerable ie, Action action) { foreach (var i in ie) { action(i); } } } 

También puede usar la biblioteca Newtonsoft.Json para esto y llamar a SerializeObject con el formato. Enum –

 var x = JsonConvert.SerializeObject(jsonString, Formatting.Indented); 

Documentación: serializar un objeto


Actualización –

Solo lo intenté de nuevo. Estoy bastante seguro de que esto solía funcionar; tal vez haya cambiado en una versión posterior o tal vez solo estoy imaginando cosas. De todos modos, según los comentarios a continuación, no funciona como se esperaba. Estos sí, sin embargo (recién probado en linqpad). El primero es de los comentarios, el segundo es un ejemplo que encontré en otro lugar en SO –

 void Main() { //Example 1 var t = "{\"x\":57,\"y\":57.0,\"z\":\"Yes\"}"; var obj = Newtonsoft.Json.JsonConvert.DeserializeObject(t); var f = Newtonsoft.Json.JsonConvert.SerializeObject(obj, Newtonsoft.Json.Formatting.Indented); Console.WriteLine(f); //Example 2 JToken jt = JToken.Parse(t); string formatted = jt.ToString(Newtonsoft.Json.Formatting.Indented); Console.WriteLine(formatted); //Example 2 in one line - Console.WriteLine(JToken.Parse(t).ToString(Newtonsoft.Json.Formatting.Indented)); } 

Muestra más corta para la biblioteca json.net.

 using Newtonsoft.Json; private static string format_json(string json) { dynamic parsedJson = JsonConvert.DeserializeObject(json); return JsonConvert.SerializeObject(parsedJson, Formatting.Indented); } 

PD: puede envolver el texto json formateado con una etiqueta para imprimir tal como está en la página html.

Aquí hay una versión compacta de un embellecedor JSON.

 private const string INDENT_STRING = " "; static string FormatJson(string json) { int indentation = 0; int quoteCount = 0; var result = from ch in json let quotes = ch == '"' ? quoteCount++ : quoteCount let lineBreak = ch == ',' && quotes % 2 == 0 ? ch + Environment.NewLine + String.Concat(Enumerable.Repeat(INDENT_STRING, indentation)) : null let openChar = ch == '{' || ch == '[' ? ch + Environment.NewLine + String.Concat(Enumerable.Repeat(INDENT_STRING, ++indentation)) : ch.ToString() let closeChar = ch == '}' || ch == ']' ? Environment.NewLine + String.Concat(Enumerable.Repeat(INDENT_STRING, --indentation)) + ch : ch.ToString() select lineBreak == null ? openChar.Length > 1 ? openChar : closeChar : lineBreak; return String.Concat(result); } 

Productos:

  { "status":"OK", "results":[ { "types":[ "locality", "political" ], "formatted_address":"New York, NY, USA", "address_components":[ { "long_name":"New York", "short_name":"New York", "types":[ "locality", "political" ] }, { "long_name":"New York", "short_name":"New York", "types":[ "administrative_area_level_2", "political" ] }, { "long_name":"New York", "short_name":"NY", "types":[ "administrative_area_level_1", "political" ] }, { "long_name":"United States", "short_name":"US", "types":[ "country", "political" ] } ], "geometry":{ "location":{ "lat":40.7143528, "lng":-74.0059731 }, "location_type":"APPROXIMATE", "viewport":{ "southwest":{ "lat":40.5788964, "lng":-74.2620919 }, "northeast":{ "lat":40.8495342, "lng":-73.7498543 } }, "bounds":{ "southwest":{ "lat":40.4773990, "lng":-74.2590900 }, "northeast":{ "lat":40.9175770, "lng":-73.7002720 } } } } ] } 

Incluso el más simple que acabo de escribir:

 public class JsonFormatter { public static string Indent = " "; public static string PrettyPrint(string input) { var output = new StringBuilder(input.Length * 2); char? quote = null; int depth = 0; for(int i=0; i 

Extensiones necesarias:

  public static string Repeat(this string str, int count) { return new StringBuilder().Insert(0, str, count).ToString(); } public static bool IsEscaped(this string str, int index) { bool escaped = false; while (index > 0 && str[--index] == '\\') escaped = !escaped; return escaped; } public static bool IsEscaped(this StringBuilder str, int index) { return str.ToString().IsEscaped(index); } 

Muestra de salida:

 { "status" : "OK", "results" : [ { "types" : [ "locality", "political" ], "formatted_address" : "New York, NY, USA", "address_components" : [ { "long_name" : "New York", "short_name" : "New York", "types" : [ "locality", "political" ] }, { "long_name" : "New York", "short_name" : "New York", "types" : [ "administrative_area_level_2", "political" ] }, { "long_name" : "New York", "short_name" : "NY", "types" : [ "administrative_area_level_1", "political" ] }, { "long_name" : "United States", "short_name" : "US", "types" : [ "country", "political" ] } ], "geometry" : { "location" : { "lat" : 40.7143528, "lng" : -74.0059731 }, "location_type" : "APPROXIMATE", "viewport" : { "southwest" : { "lat" : 40.5788964, "lng" : -74.2620919 }, "northeast" : { "lat" : 40.8495342, "lng" : -73.7498543 } }, "bounds" : { "southwest" : { "lat" : 40.4773990, "lng" : -74.2590900 }, "northeast" : { "lat" : 40.9175770, "lng" : -73.7002720 } } } } ] } 

Necesita omitir \r y \n en PrettyPrint() . La salida parece divertida de que ya hay algunos crlf (o la fuente ya estaba formateada).

Lo solucionó … de alguna manera.

 public class JsonFormatter { #region class members const string Space = " "; const int DefaultIndent = 0; const string Indent = Space + Space + Space + Space; static readonly string NewLine = Environment.NewLine; #endregion private enum JsonContextType { Object, Array } static void BuildIndents(int indents, StringBuilder output) { indents += DefaultIndent; for (; indents > 0; indents--) output.Append(Indent); } bool inDoubleString = false; bool inSingleString = false; bool inVariableAssignment = false; char prevChar = '\0'; Stack context = new Stack(); bool InString() { return inDoubleString || inSingleString; } public string PrettyPrint(string input) { var output = new StringBuilder(input.Length * 2); char c; for (int i = 0; i < input.Length; i++) { c = input[i]; switch (c) { case '{': if (!InString()) { if (inVariableAssignment || (context.Count > 0 && context.Peek() != JsonContextType.Array)) { output.Append(NewLine); BuildIndents(context.Count, output); } output.Append(c); context.Push(JsonContextType.Object); output.Append(NewLine); BuildIndents(context.Count, output); } else output.Append(c); break; case '}': if (!InString()) { output.Append(NewLine); context.Pop(); BuildIndents(context.Count, output); output.Append(c); } else output.Append(c); break; case '[': output.Append(c); if (!InString()) context.Push(JsonContextType.Array); break; case ']': if (!InString()) { output.Append(c); context.Pop(); } else output.Append(c); break; case '=': output.Append(c); break; case ',': output.Append(c); if (!InString() && context.Peek() != JsonContextType.Array) { BuildIndents(context.Count, output); output.Append(NewLine); BuildIndents(context.Count, output); inVariableAssignment = false; } break; case '\'': if (!inDoubleString && prevChar != '\\') inSingleString = !inSingleString; output.Append(c); break; case ':': if (!InString()) { inVariableAssignment = true; output.Append(Space); output.Append(c); output.Append(Space); } else output.Append(c); break; case '"': if (!inSingleString && prevChar != '\\') inDoubleString = !inDoubleString; output.Append(c); break; case ' ': if (InString()) output.Append(c); break; default: output.Append(c); break; } prevChar = c; } return output.ToString(); } } 

crédito [enlace muerto]

Ya hay un montón de excelentes respuestas aquí que usan Newtonsoft.JSON , pero aquí hay una más que usa JObject.Parse en combinación con ToString() , ya que aún no se ha mencionado:

 var jObj = Newtonsoft.Json.Linq.JObject.Parse(json); var formatted = jObj.ToString(Newtonsoft.Json.Formatting.Indented); 

Esta es una variante de la respuesta aceptada que me gusta usar. Las partes comentadas resultan en lo que considero un formato más legible (necesitaría comentar el código adyacente para ver la diferencia):

 public class JsonHelper { private const int INDENT_SIZE = 4; public static string FormatJson(string str) { str = (str ?? "").Replace("{}", @"\{\}").Replace("[]", @"\[\]"); var inserts = new List(); bool quoted = false, escape = false; int depth = 0/*-1*/; for (int i = 0, N = str.Length; i < N; i++) { var chr = str[i]; if (!escape && !quoted) switch (chr) { case '{': case '[': inserts.Add(new[] { i, +1, 0, INDENT_SIZE * ++depth }); //int n = (i == 0 || "{[,".Contains(str[i - 1])) ? 0 : -1; //inserts.Add(new[] { i, n, INDENT_SIZE * ++depth * -n, INDENT_SIZE - 1 }); break; case ',': inserts.Add(new[] { i, +1, 0, INDENT_SIZE * depth }); //inserts.Add(new[] { i, -1, INDENT_SIZE * depth, INDENT_SIZE - 1 }); break; case '}': case ']': inserts.Add(new[] { i, -1, INDENT_SIZE * --depth, 0 }); //inserts.Add(new[] { i, -1, INDENT_SIZE * depth--, 0 }); break; case ':': inserts.Add(new[] { i, 0, 1, 1 }); break; } quoted = (chr == '"') ? !quoted : quoted; escape = (chr == '\\') ? !escape : false; } if (inserts.Count > 0) { var sb = new System.Text.StringBuilder(str.Length * 2); int lastIndex = 0; foreach (var insert in inserts) { int index = insert[0], before = insert[2], after = insert[3]; bool nlBefore = (insert[1] == -1), nlAfter = (insert[1] == +1); sb.Append(str.Substring(lastIndex, index - lastIndex)); if (nlBefore) sb.AppendLine(); if (before > 0) sb.Append(new String(' ', before)); sb.Append(str[index]); if (nlAfter) sb.AppendLine(); if (after > 0) sb.Append(new String(' ', after)); lastIndex = index + 1; } str = sb.ToString(); } return str.Replace(@"\{\}", "{}").Replace(@"\[\]", "[]"); } }