JSON格式化在C#?
寻找一个函数,将string
Json作为input,并用换行符和缩进来格式化它。 validation将是一个奖金,但没有必要,我不需要parsing成一个对象或任何东西。
任何人都知道这样的图书馆?
示例input:
{"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}}}}]}
我更新了旧版本,现在它应该支持诸如整数和布尔值之类的未加引号的值。
我重构了以前的版本,并得到最终版本:代码更短,更干净。 只需要一个扩展方法。 最重要的是:修复一些错误。
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<T>(this IEnumerable<T> ie, Action<T> action) { foreach (var i in ie) { action(i); } } }
你也可以使用Newtonsoft.Json库来调用SerializeObject和Formatting.Indented枚举 –
var x = JsonConvert.SerializeObject(jsonString, Formatting.Indented);
文档: 序列化对象
更新 –
只是试了一遍。 很确定,这曾经工作 – 也许它在后来的版本改变,也许我只是想象的东西。 无论如何,根据下面的评论,它不像预期的那样工作。 这些做,但是(只在linqpadtesting)。 第一个是来自评论,第二个是我在其他地方发现的一个例子 –
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)); }
json.net库的样本较短。
using Newtonsoft.Json; private static string format_json(string json) { dynamic parsedJson = JsonConvert.DeserializeObject(json); return JsonConvert.SerializeObject(parsedJson, Formatting.Indented); }
PS:你可以用格式打包格式化的json文本来打印,因为它在html页面上。
这是一个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); }
输出:
{ "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 } } } } ] }
更简单的我刚写道:
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<input.Length; ++i) { char ch = input[i]; switch (ch) { case '{': case '[': output.Append(ch); if (!quote.HasValue) { output.AppendLine(); output.Append(Indent.Repeat(++depth)); } break; case '}': case ']': if (quote.HasValue) output.Append(ch); else { output.AppendLine(); output.Append(Indent.Repeat(--depth)); output.Append(ch); } break; case '"': case '\'': output.Append(ch); if (quote.HasValue) { if (!output.IsEscaped(i)) quote = null; } else quote = ch; break; case ',': output.Append(ch); if (!quote.HasValue) { output.AppendLine(); output.Append(Indent.Repeat(depth)); } break; case ':': if (quote.HasValue) output.Append(ch); else output.Append(" : "); break; default: if (quote.HasValue || !char.IsWhiteSpace(ch)) output.Append(ch); break; } } return output.ToString(); } }
必要的扩展:
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); }
示例输出:
{ "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 } } } } ] }
您需要跳过PrettyPrint()
\r
和\n
。 输出看起来很有趣,有一些crlf已经存在(或源已经格式化)。
修正了…有点。
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<JsonContextType> context = new Stack<JsonContextType>(); 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(); } }
信用 [死链接]
这是我喜欢使用的接受答案的变体。 评论的部分导致我认为更可读的格式(您需要注释掉相邻的代码才能看到差异):
public class JsonHelper { private const int INDENT_SIZE = 4; public static string FormatJson(string str) { str = (str ?? "").Replace("{}", @"\{\}").Replace("[]", @"\[\]"); var inserts = new List<int[]>(); 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(@"\[\]", "[]"); } }
正如benjymous所指出的,你可以使用Newtonsoft.Json和一个临时对象并反序列化/序列化。
var obj = JsonConvert.DeserializeObject(jsonString); var formatted = JsonConvert.SerializeObject(obj, Formatting.Indented);
这将把每个项目放在一个新的线上
VB.NET
mytext = responseFromServer.Replace("{", vbNewLine + "{")
C#
mytext = responseFromServer.Replace("{", Environment.Newline + "{")
编写自己的函数的主要原因是JSON框架通常将stringparsing为.nettypes并将其转换回string,这可能会导致丢失原始string。 例如0.0002变成2E-4
我不发布我的function(这里和其他的一样),但这里是testing用例
using System.IO; using Newtonsoft.Json; using NUnit.Framework; namespace json_formatter.tests { [TestFixture] internal class FormatterTests { [Test] public void CompareWithNewtonsofJson() { string file = Path.Combine(TestContext.CurrentContext.TestDirectory, "json", "minified.txt"); string json = File.ReadAllText(file); string newton = JsonPrettify(json); // Double space are indent symbols which newtonsoft framework uses string my = new Formatter(" ").Format(json); Assert.AreEqual(newton, my); } [Test] public void EmptyArrayMustNotBeFormatted() { var input = "{\"na{me\": []}"; var expected = "{\r\n\t\"na{me\": []\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } [Test] public void EmptyObjectMustNotBeFormatted() { var input = "{\"na{me\": {}}"; var expected = "{\r\n\t\"na{me\": {}\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } [Test] public void MustAddLinebreakAfterBraces() { var input = "{\"name\": \"value\"}"; var expected = "{\r\n\t\"name\": \"value\"\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } [Test] public void MustFormatNestedObject() { var input = "{\"na{me\":\"val}ue\", \"name1\": {\"name2\":\"value\"}}"; var expected = "{\r\n\t\"na{me\": \"val}ue\",\r\n\t\"name1\": {\r\n\t\t\"name2\": \"value\"\r\n\t}\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } [Test] public void MustHandleArray() { var input = "{\"name\": \"value\", \"name2\":[\"a\", \"b\", \"c\"]}"; var expected = "{\r\n\t\"name\": \"value\",\r\n\t\"name2\": [\r\n\t\t\"a\",\r\n\t\t\"b\",\r\n\t\t\"c\"\r\n\t]\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } [Test] public void MustHandleArrayOfObject() { var input = "{\"name\": \"value\", \"name2\":[{\"na{me\":\"val}ue\"}, {\"nam\\\"e2\":\"val\\\\\\\"ue\"}]}"; var expected = "{\r\n\t\"name\": \"value\",\r\n\t\"name2\": [\r\n\t\t{\r\n\t\t\t\"na{me\": \"val}ue\"\r\n\t\t},\r\n\t\t{\r\n\t\t\t\"nam\\\"e2\": \"val\\\\\\\"ue\"\r\n\t\t}\r\n\t]\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } [Test] public void MustHandleEscapedString() { var input = "{\"na{me\":\"val}ue\", \"name1\": {\"nam\\\"e2\":\"val\\\\\\\"ue\"}}"; var expected = "{\r\n\t\"na{me\": \"val}ue\",\r\n\t\"name1\": {\r\n\t\t\"nam\\\"e2\": \"val\\\\\\\"ue\"\r\n\t}\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } [Test] public void MustIgnoreEscapedQuotesInsideString() { var input = "{\"na{me\\\"\": \"val}ue\"}"; var expected = "{\r\n\t\"na{me\\\"\": \"val}ue\"\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } [TestCase(" ")] [TestCase("\"")] [TestCase("{")] [TestCase("}")] [TestCase("[")] [TestCase("]")] [TestCase(":")] [TestCase(",")] public void MustIgnoreSpecialSymbolsInsideString(string symbol) { string input = "{\"na" + symbol + "me\": \"val" + symbol + "ue\"}"; string expected = "{\r\n\t\"na" + symbol + "me\": \"val" + symbol + "ue\"\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } [Test] public void StringEndsWithEscapedBackslash() { var input = "{\"na{me\\\\\": \"val}ue\"}"; var expected = "{\r\n\t\"na{me\\\\\": \"val}ue\"\r\n}"; Assert.AreEqual(expected, new Formatter().Format(input)); } private static string PrettifyUsingNewtosoft(string json) { using (var stringReader = new StringReader(json)) using (var stringWriter = new StringWriter()) { var jsonReader = new JsonTextReader(stringReader); var jsonWriter = new JsonTextWriter(stringWriter) { Formatting = Formatting.Indented }; jsonWriter.WriteToken(jsonReader); return stringWriter.ToString(); } } } }
J Bryan Price就是一个很好的例子,但也有缺点
{\"response\":[123, 456, {\"name\":\"John\"}, {\"count\":3}]}
格式化后
{ "response" : [ 123, 456, { "name" : "John" }, { "count" : 3 } ] }
不当的偏见:(
例
public static string JsonFormatter(string json) { StringBuilder builder = new StringBuilder(); bool quotes = false; bool ignore = false; int offset = 0; int position = 0; if (string.IsNullOrEmpty(json)) { return string.Empty; } json = json.Replace(Environment.NewLine, "").Replace("\t", ""); foreach (char character in json) { switch (character) { case '"': if (!ignore) { quotes = !quotes; } break; case '\'': if (quotes) { ignore = !ignore; } break; } if (quotes) { builder.Append(character); } else { switch (character) { case '{': case '[': builder.Append(character); builder.Append(Environment.NewLine); builder.Append(new string(' ', ++offset * 4)); break; case '}': case ']': builder.Append(Environment.NewLine); builder.Append(new string(' ', --offset * 4)); builder.Append(character); break; case ',': builder.Append(character); builder.Append(Environment.NewLine); builder.Append(new string(' ', offset * 4)); break; case ':': builder.Append(character); builder.Append(' '); break; default: if (character != ' ') { builder.Append(character); } break; } position++; } } return builder.ToString().Trim(); }
所有学分都是给Frank Tzanabetis。 但是这是最短的直接例子,在空string或破坏的原始JSONstring的情况下也能够存活:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; ... private static string Format(string jsonString) { try { return JToken.Parse(jsonString).ToString(Formatting.Indented); } catch { return jsonString; } }