C# 中的 JSON 格式化程序?
Posted
技术标签:
【中文标题】C# 中的 JSON 格式化程序?【英文标题】:JSON formatter in C#? 【发布时间】:2011-06-02 14:20:33 【问题描述】:寻找一个函数,它将 Json 的string
作为输入并使用换行符和缩进对其进行格式化。验证将是一个奖励,但不是必需的,我不需要将其解析为对象或任何东西。
有人知道这样的图书馆吗?
示例输入:
"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]
【问题讨论】:
How do I get formatted JSON in .NET using C#?的可能重复 【参考方案1】:您也可以为此使用 Newtonsoft.Json 库并使用 Formatting.Indented 枚举调用 SerializeObject -
var x = JsonConvert.SerializeObject(jsonString, Formatting.Indented);
文档:Serialize an Object
更新-
刚刚又试了一次。很确定这曾经有效 - 也许它在后续版本中发生了变化,或者我只是在想象一些事情。无论如何,根据下面的 cmets,它并没有按预期工作。但是,这些确实如此(刚刚在 linqpad 中测试过)。第一个来自 cmets,第二个是我在 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));
【讨论】:
Vince - 是的,但如果你要这样做,这可能意味着你还要做其他 JSON 的东西,如果是这样,那将是有意义的。即使没有,我认为在大多数情况下它仍然比滚动自己的漂亮打印机要好,因为这样做需要更多的努力:) 这不起作用。以这种方式序列化已经是 json 的字符串不会美化它,即使指定了 Formatting.Indented。它只是简单地引用字符串,并转义所有现有的引号。 罗斯 - 你试过了吗?当您使用 Formatting.Indented 选项时,它会“漂亮地打印”JSON 字符串。 我尝试了上面的代码,得到了相同的(错误的)结果——对我的修复是:var obj = JsonConvert.DeserializeObject(jsonString); var formatted = JsonConvert.SerializeObject(obj, Formatting.Indented)
(即反序列化为临时对象,然后返回 json)——真的不是最有效的方法,但至少确实有效!【参考方案2】:
我更新了旧版本,现在它应该支持不带引号的值,例如整数和布尔值。
我重构了之前的版本,得到了最终版本: 代码更短更干净。只需要一种扩展方法。最重要的:修复了一些错误。
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);
【讨论】:
哦.. 我在看我的旧版本。那好吧。还是不错的 :D 显然我还没有接受答案,所以 GJ!你得到支票。 你的也不错,除了一个小错误:"url":"url('http://google.com')"
格式为"url":"url('http : //google.com')"
。第二个“:”前后加空格,这是错误的。
这个真的是在处理整数等不带引号的值吗?
为什么不在IEnumerable
上使用.ToList()
而不是创建一个新方法?如果您在项目中使用 MoreLinq
,这也将支持 .ForEach()
on IEnumerable
开箱即用。【参考方案3】:
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 页面上打印。
【讨论】:
使用 newtonsoft.Json 版本 6 非常适合我。 适用于 newtonsoft.Json 版本 10.0.3。在 Win10 Intel i7-7700 CPU (4.20Ghz) 上在 5 秒内格式化 6MB JSON 文件。【参考方案4】:这对我在 .Net Core 3.1 中使用 System.Text.Json 有效
public string PrettyJson(string unPrettyJson)
var options = new JsonSerializerOptions()
WriteIndented = true
;
var jsonElement = JsonSerializer.Deserialize<JsonElement>(unPrettyJson);
return JsonSerializer.Serialize(jsonElement, options);
【讨论】:
酷!貌似是 .NET Core 3.0 中添加的,2019 年 9 月 23 日发布 请注意,现在建议使用 System.Text.Json 而不是 Newtonsoft。这将是正确的实现。 完美的软件,在 net5、net6、...中运行良好...【参考方案5】:这是 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
]
【讨论】:
输出每隔行 1 个空格,冒号后可以使用一些空格。 我不敢相信@Vince_Panucio 的回答只得到了 3 票?这是纯粹的天才。将他的 linq 代码粘贴到 Visual Studio 中,然后使用 resharper 将其转换为方法链,看看您如何使用普通的 .Select(x...).Select(y) 编写相同的东西,它是一些页长。干得好文斯,……干得好! +1 代表精湛的工艺。尽管我很喜欢它,但对于(共享)生产代码,为了可读性/可调试性,我可能会将其拆分并转换为 foreach 循环。 @mpen 额外的空格似乎是原始输入中逗号之后和引号之前的空格。所以,根据你的输入,ymmv。 这不是完美的家伙,请随意在自己的项目中修复它【参考方案6】:Vince Panuccio 的紧凑型 JSON formatter 给我留下了深刻的印象。 这是我现在使用的改进版本:
public static string FormatJson(string json, string indent = " ")
var indentation = 0;
var quoteCount = 0;
var escapeCount = 0;
var result =
from ch in json ?? string.Empty
let escaped = (ch == '\\' ? escapeCount++ : escapeCount > 0 ? escapeCount-- : escapeCount) > 0
let quotes = ch == '"' && !escaped ? quoteCount++ : quoteCount
let unquoted = quotes % 2 == 0
let colon = ch == ':' && unquoted ? ": " : null
let nospace = char.IsWhiteSpace(ch) && unquoted ? string.Empty : null
let lineBreak = ch == ',' && unquoted ? ch + Environment.NewLine + string.Concat(Enumerable.Repeat(indent, indentation)) : null
let openChar = (ch == '' || ch == '[') && unquoted ? ch + Environment.NewLine + string.Concat(Enumerable.Repeat(indent, ++indentation)) : ch.ToString()
let closeChar = (ch == '' || ch == ']') && unquoted ? Environment.NewLine + string.Concat(Enumerable.Repeat(indent, --indentation)) + ch : ch.ToString()
select colon ?? nospace ?? lineBreak ?? (
openChar.Length > 1 ? openChar : closeChar
);
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
]
【讨论】:
这是一个不错的解决方案。 这里的最佳解决方案。【参考方案7】:所有学分均归功于 Frank Tzanabetics。然而,这是最短的直接示例,在空字符串或原始 JSON 字符串损坏的情况下也能继续存在:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
...
try
return JToken.Parse(jsonString).ToString(Formatting.Indented);
catch
return jsonString;
【讨论】:
【参考方案8】:我刚刚写的更简单:
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
]
【讨论】:
一个小错误:"url":"url('http://google.com')"
将被格式化为"url":"url('http : //google.com')"
。【参考方案9】:
这里已经有很多很棒的答案使用Newtonsoft.JSON,但这里还有一个使用JObject.Parse
和ToString()
的答案,因为尚未提及:
var jObj = Newtonsoft.Json.Linq.JObject.Parse(json);
var formatted = jObj.ToString(Newtonsoft.Json.Formatting.Indented);
【讨论】:
这应该是答案.. 只有两行但仅适用于变量 json 是 json-Object 的情况; else 如果参数是数组、字符串、null 等,解析可能会失败。【参考方案10】:只需使用JsonDocument
和Utf8JsonWriter
。 不需要第三方库。 jsonString
不需要反序列化目标对象。
using System.IO;
using System.Text;
using System.Text.Json;
// other code ...
public string Prettify(string jsonString)
using var stream = new MemoryStream();
var document = JsonDocument.Parse(jsonString);
var writer = new Utf8JsonWriter(stream, new JsonWriterOptions Indented = true );
document.WriteTo(writer);
writer.Flush();
return Encoding.UTF8.GetString(stream.ToArray());
【讨论】:
你认为JsonDocument.Parse
是做什么的?肯定会反序列化它吗?
@mpen,理想情况下不应该这样,因为我们没有指定要反序列化的类型。但它有可能在内部将其反序列化为 dynamic
类型。让我们希望这不是它正在做的事情。 :)【参考方案11】:
正如benjymous 指出的那样,您可以将Newtonsoft.Json 与临时对象一起使用并反序列化/序列化。
var obj = JsonConvert.DeserializeObject(jsonString);
var formatted = JsonConvert.SerializeObject(obj, Formatting.Indented);
【讨论】:
@dvdmn allredy 两年前发布了相同的答案【参考方案12】:编写自己的函数的主要原因是JSON框架通常将字符串解析为.net类型并将它们转换回字符串,这可能会导致丢失原始字符串。例如 0.0002 变成 2E-4
我没有发布我的功能(它和这里的其他功能非常相似)但这里是测试用例
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 = "\"name\": []";
var expected = "\r\n\t\"name\": []\r\n";
Assert.AreEqual(expected, new Formatter().Format(input));
[Test]
public void EmptyObjectMustNotBeFormatted()
var input = "\"name\": ";
var expected = "\r\n\t\"name\": \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 = "\"name\":\"value\", \"name1\": \"name2\":\"value\"";
var expected = "\r\n\t\"name\": \"value\",\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\":[\"name\":\"value\", \"nam\\\"e2\":\"val\\\\\\\"ue\"]";
var expected =
"\r\n\t\"name\": \"value\",\r\n\t\"name2\": [\r\n\t\t\r\n\t\t\t\"name\": \"value\"\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 = "\"name\":\"value\", \"name1\": \"nam\\\"e2\":\"val\\\\\\\"ue\"";
var expected = "\r\n\t\"name\": \"value\",\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 = "\"name\\\"\": \"value\"";
var expected = "\r\n\t\"name\\\"\": \"value\"\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 = "\"name\\\\\": \"value\"";
var expected = "\r\n\t\"name\\\\\": \"value\"\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();
【讨论】:
【参考方案13】:您需要跳过PrettyPrint()
中的\r
和\n
。输出看起来很有趣,因为已经存在一些 crlf(或者源已经格式化)。
【讨论】:
【参考方案14】:修复它...有点。
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();
credit [死链接]
【讨论】:
【参考方案15】:这会将每个项目放在一个新行上
VB.NET
mytext = responseFromServer.Replace("", vbNewLine + "")
C#
mytext = responseFromServer.Replace("", Environment.NewLine + "");
【讨论】:
【参考方案16】:使用简单的方法扩展:
using Newtonsoft.Json;
namespace Example
public static class StringExtensions
public static string BeautifyJson(this string str)
var obj = JsonConvert.DeserializeObject(str);
string json = JsonConvert.SerializeObject(obj, Formatting.Indented);
return json;
并像这样使用它:
string jsonFormatted = json.BeautifyJson();
【讨论】:
【参考方案17】:这是我喜欢使用的公认答案的变体。注释部分导致我认为更具可读性的格式(您需要注释掉相邻的代码才能看到差异):
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(@"\[\]", "[]");
【讨论】:
【参考方案18】:例子
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();
【讨论】:
【参考方案19】:此版本生成的 JSON 更紧凑,在我看来更具可读性,因为您一次可以看到更多内容。它通过格式化最深层内联或类似紧凑的数组结构来做到这一点。
代码没有依赖但更复杂。
"name":"Seller",
"schema":"dbo",
"CaptionFields":["Caption","Id"],
"fields":[
"name":"Id","type":"Integer","length":"10","autoincrement":true,"nullable":false,
"name":"FirstName","type":"Text","length":"50","autoincrement":false,"nullable":false,
"name":"LastName","type":"Text","length":"50","autoincrement":false,"nullable":false,
"name":"LotName","type":"Text","length":"50","autoincrement":false,"nullable":true,
"name":"LotDetailsURL","type":"Text","length":"255","autoincrement":false,"nullable":true
]
代码如下
private class IndentJsonInfo
public IndentJsonInfo(string prefix, char openingTag)
Prefix = prefix;
OpeningTag = openingTag;
Data = new List<string>();
public string Prefix;
public char OpeningTag;
public bool isOutputStarted;
public List<string> Data;
internal static string IndentJSON(string jsonString, int startIndent = 0, int indentSpaces = 2)
if (String.IsNullOrEmpty(jsonString))
return jsonString;
try
var jsonCache = new List<IndentJsonInfo>();
IndentJsonInfo currentItem = null;
var sbResult = new StringBuilder();
int curIndex = 0;
bool inQuotedText = false;
var chunk = new StringBuilder();
var saveChunk = new Action(() =>
if (chunk.Length == 0)
return;
if (currentItem == null)
throw new Exception("Invalid JSON: No container.");
currentItem.Data.Add(chunk.ToString());
chunk = new StringBuilder();
);
while (curIndex < jsonString.Length)
var cChar = jsonString[curIndex];
if (inQuotedText)
// Get the rest of quoted text.
chunk.Append(cChar);
// Determine if the quote is escaped.
bool isEscaped = false;
var excapeIndex = curIndex;
while (excapeIndex > 0 && jsonString[--excapeIndex] == '\\') isEscaped = !isEscaped;
if (cChar == '"' && !isEscaped)
inQuotedText = false;
else if (Char.IsWhiteSpace(cChar))
// Ignore all whitespace outside of quotes.
else
// Outside of Quotes.
switch (cChar)
case '"':
chunk.Append(cChar);
inQuotedText = true;
break;
case ',':
chunk.Append(cChar);
saveChunk();
break;
case '':
case '[':
currentItem = new IndentJsonInfo(chunk.ToString(), cChar);
jsonCache.Add(currentItem);
chunk = new StringBuilder();
break;
case '':
case ']':
saveChunk();
for (int i = 0; i < jsonCache.Count; i++)
var item = jsonCache[i];
var isLast = i == jsonCache.Count - 1;
if (!isLast)
if (!item.isOutputStarted)
sbResult.AppendLine(
"".PadLeft((startIndent + i) * indentSpaces) +
item.Prefix + item.OpeningTag);
item.isOutputStarted = true;
var newIndentString = "".PadLeft((startIndent + i + 1) * indentSpaces);
foreach (var listItem in item.Data)
sbResult.AppendLine(newIndentString + listItem);
item.Data = new List<string>();
else // If Last
if (!(
(item.OpeningTag == '' && cChar == '') ||
(item.OpeningTag == '[' && cChar == ']')
))
throw new Exception("Invalid JSON: Container Mismatch, Open '" + item.OpeningTag + "', Close '" + cChar + "'.");
string closing = null;
if (item.isOutputStarted)
var newIndentString = "".PadLeft((startIndent + i + 1) * indentSpaces);
foreach (var listItem in item.Data)
sbResult.AppendLine(newIndentString + listItem);
closing = cChar.ToString();
else
closing =
item.Prefix + item.OpeningTag +
String.Join("", currentItem.Data.ToArray()) +
cChar;
jsonCache.RemoveAt(i);
currentItem = (jsonCache.Count > 0) ? jsonCache[jsonCache.Count - 1] : null;
chunk.Append(closing);
break;
default:
chunk.Append(cChar);
break;
curIndex++;
if (inQuotedText)
throw new Exception("Invalid JSON: Incomplete Quote");
else if (jsonCache.Count != 0)
throw new Exception("Invalid JSON: Incomplete Structure");
else
if (chunk.Length > 0)
sbResult.AppendLine("".PadLeft(startIndent * indentSpaces) + chunk);
var result = sbResult.ToString();
return result;
catch (Exception ex)
throw; // Comment out to return unformatted text if the format failed.
// Invalid JSON, skip the formatting.
return jsonString;
该函数允许您指定缩进的起点,因为我将其用作组装非常大的 JSON 格式备份文件的过程的一部分。
【讨论】:
以上是关于C# 中的 JSON 格式化程序?的主要内容,如果未能解决你的问题,请参考以下文章