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.ParseToString() 的答案,因为尚未提及:

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

【讨论】:

这应该是答案.. 只有两行但仅适用于变量 json 是 json-Object 的情况; else 如果参数是数组、字符串、null 等,解析可能会失败。【参考方案10】:

只需使用JsonDocumentUtf8JsonWriter不需要第三方库。 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 格式化程序?的主要内容,如果未能解决你的问题,请参考以下文章

C# 把list中的数据转成规定格式的json格式

C#编写上位机使用UDP给单片机发送Json格式数据(完整程序)

C# 解析JSON格式数据

C# JSON格式数据用法

以gridview格式显示JSON C#

用C#语言将json格式数据转成json对象