【问题标题】:JSON formatter in C#?C# 中的 JSON 格式化程序?
【发布时间】: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}}}}]} 

【问题讨论】:

标签: c# json formatting


【解决方案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);
}

它修复了以下问题:

  1. 字符串中的转义序列
  2. 冒号后缺少空格
  3. 逗号后(或其他地方)的额外空格
  4. 字符串中的方括号和花括号
  5. 在空输入时不会失败

输出:

{
  "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 = "{\"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();
                }
            }
        }
    }
    

    【讨论】:

      【解决方案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 格式备份文件的过程的一部分。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2013-09-07
                    • 2020-05-10
                    • 2020-01-24
                    • 2022-07-04
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2023-01-18
                    相关资源
                    最近更新 更多