JSON formatter di C #?

101

Mencari fungsi yang akan menggunakan stringJson sebagai input dan memformatnya dengan jeda baris dan indentasi. Validasi akan menjadi bonus, tetapi tidak perlu, dan saya tidak perlu menguraikannya menjadi objek atau apa pun.

Adakah yang tahu tentang perpustakaan seperti itu?


Contoh masukan:

{"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}}}}]} 
mpen
sumber

Jawaban:

123

Saya memperbarui versi lama, sekarang harus mendukung nilai yang tidak dikutip seperti integer dan boolean.

Saya merefaktor versi sebelumnya dan mendapatkan versi terakhir: Kode ini lebih pendek dan lebih bersih. Hanya membutuhkan satu metode ekstensi. Yang paling penting: memperbaiki beberapa bug.

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);
        }
    }
}
Peter Long
sumber
Oh .. Saya melihat versi lama saya. Baiklah. Masih bagus: D Rupanya saya belum menerima jawaban, jadi GJ! Anda mendapatkan ceknya.
mpen
Milik Anda juga bagus kecuali satu bug kecil: "url":"url('http://google.com')"format ke "url":"url('http : //google.com')". spasi ditambahkan sebelum dan setelah ":" kedua yang salah.
Peter Long
Apakah yang ini benar-benar berfungsi pada nilai yang tidak dikutip seperti bilangan bulat?
Johan Danforth
@JohanDanforth - setelah saya menghapus baris # 64 (bit "jika (dikutip)") tampaknya berfungsi dengan baik untuk saya dengan nilai yang tidak dikutip.
jerhewet
mengapa tidak digunakan .ToList()di IEnumerablealih - alih membuat metode baru? jika Anda menggunakan MoreLinqdalam proyek Anda, ini juga akan mendukung .ForEach()di IEnumerableluar kotak.
Kehlan Krumme
123

Anda juga bisa menggunakan perpustakaan Newtonsoft.Json untuk ini dan memanggil SerializeObject dengan enum Formatting.Indented -

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

Dokumentasi: Menyusun Objek


Perbarui -

Coba lagi. Cukup yakin ini dulu berfungsi - mungkin berubah di versi berikutnya atau mungkin saya hanya membayangkan sesuatu. Bagaimanapun, sesuai komentar di bawah, itu tidak berfungsi seperti yang diharapkan. Namun demikian (baru saja diuji di linqpad). Yang pertama adalah dari komentar, yang kedua adalah contoh yang saya temukan di tempat lain di 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));
}
Frank Tzanabetis
sumber
8
Vince - benar, tetapi jika Anda akan melakukan itu mungkin berarti Anda akan melakukan hal-hal JSON lainnya juga dan jika demikian itu masuk akal. Bahkan jika tidak, saya berpendapat itu masih lebih baik daripada meluncurkan printer cantik Anda sendiri dalam banyak kasus karena melakukan itu membutuhkan lebih banyak usaha :)
Frank Tzanabetis
6
Ini tidak bekerja. Membuat serial string yang sudah json dengan cara ini tidak akan mempercantiknya, bahkan dengan Formatting.Indented ditentukan. Ini hanya mengutip string, dan melepaskan semua kutipan yang ada.
Ross
Ross - apakah kamu sudah mencobanya? Ketika Anda menggunakan opsi Formatting.Indented dengannya, itu "cukup mencetak" string JSON.
Frank Tzanabetis
8
Saya mencoba kode di atas, dan mendapatkan hasil yang sama (salah) - perbaikan untuk saya adalah: var obj = JsonConvert.DeserializeObject(jsonString); var formatted = JsonConvert.SerializeObject(obj, Formatting.Indented) (yaitu deserialise ke objek temp, lalu kembali ke json) - benar-benar bukan metode yang paling efisien, tetapi itu setidaknya berhasil!
benjymous
59

Sampel yang lebih pendek untuk pustaka json.net.

using Newtonsoft.Json;

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

NB: Anda dapat membungkus teks json yang diformat dengan tag untuk dicetak seperti pada halaman html.

dvdmn
sumber
Bekerja sangat baik untuk saya dengan newtonsoft.Json versi 6.
Rocklan
Bekerja dengan baik dengan newtonsoft.Json versi 10.0.3. Memformat file JSON 6MB dalam waktu kurang dari 5 detik pada CPU Win10 Intel i7-7700 (4.20Ghz).
batpox
35

Berikut adalah versi ringkas dari penghias 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);
}

Keluaran:

 {
    "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
                    }
                }
            }
        }
    ]
}
Pisau cukur
sumber
2
Output dimatikan sebesar 1 spasi di setiap baris lainnya, dan dapat menggunakan beberapa spasi setelah titik dua.
mpen
6
Aku tidak percaya jawaban @Vince_Panucio hanya mendapat 3 suara positif? Benar-benar jenius. Ambil kode linq-nya dan paste ke visual studio, lalu gunakan resharper untuk mengubahnya menjadi rantai metode, untuk melihat bagaimana Anda akan menulis hal yang sama menggunakan normal. Pilih (x ...). Pilih (y) dan itu beberapa halaman panjang. Kerja bagus Vince, ... bagus sekali!
kode salju
1
1 untuk pengerjaan yang luar biasa. Sebanyak yang saya suka, untuk kode produksi (bersama) saya mungkin akan membaginya dan mengubahnya menjadi loop foreach demi keterbacaan / debuggability.
3dGrabber
1
@mpen spasi ekstra tampaknya merupakan spasi setelah koma dan sebelum kutipan di input asli. Jadi, tergantung masukan Anda, ymmv.
Jesse Chisholm
1
Ini bukan orang yang sempurna, jangan ragu untuk memperbaikinya dalam proyek Anda sendiri
Razor
8

Bahkan lebih sederhana yang baru saja saya tulis:

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();
    }
}

Ekstensi yang diperlukan:

    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);
    }

Output sampel:

{
    "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
                    }
                }
            }
        }
    ]
}
mpen
sumber
Satu bug kecil: "url":"url('http://google.com')"akan diformat ke "url":"url('http : //google.com')".
Peter Long
6

Sudah ada banyak jawaban bagus di sini yang menggunakan Newtonsoft.JSON , tapi inilah satu lagi yang digunakan JObject.Parsedalam kombinasi dengan ToString(), karena itu belum disebutkan:

var jObj = Newtonsoft.Json.Linq.JObject.Parse(json);
var formatted = jObj.ToString(Newtonsoft.Json.Formatting.Indented);
Nate Cook
sumber
Ini harus menjadi jawabannya .. hanya dua baris TAPI hanya untuk kasus ketika variabel json Anda adalah json-Object; else Parse mungkin gagal jika argumen adalah array, string, null dll ..
joedotnot
6

Saya sangat terkesan dengan formatter JSON kompak oleh Vince Panuccio .
Ini adalah versi perbaikan yang sekarang saya gunakan:

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);
}

Ini memperbaiki masalah berikut:

  1. Escape urutan di dalam string
  2. Spasi hilang setelah titik dua
  3. Spasi ekstra setelah koma (atau di tempat lain)
  4. Tanda kurung siku dan keriting di dalam senar
  5. Tidak gagal pada input null

Keluaran:

{
  "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
          }
        }
      }
    }
  ]
}
yallie
sumber
3

Alasan utama menulis fungsi Anda sendiri adalah karena framework JSON biasanya melakukan parsing string menjadi tipe .net dan mengubahnya kembali menjadi string, yang dapat mengakibatkan hilangnya string asli. Misalnya 0,0002 menjadi 2E-4

Saya tidak memposting fungsi saya (ini cukup sama dengan yang lain di sini) tetapi berikut adalah kasus uji

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();
            }
        }
    }
}
Max Venediktov
sumber
2

Anda harus melewati \rdan \nmasuk PrettyPrint(). Keluarannya terlihat lucu karena ada beberapa crlf yang sudah ada (atau sumbernya sudah diformat).

wvd_vegt
sumber
2

Memperbaiki ... agak.

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();
    }
}

kredit [tautan mati]

mpen
sumber
2

Seperti yang ditunjukkan benjymous , Anda dapat menggunakan Newtonsoft.Json dengan objek sementara dan deserialize / serialize.

var obj = JsonConvert.DeserializeObject(jsonString); 
var formatted = JsonConvert.SerializeObject(obj, Formatting.Indented);
tzipp
sumber
1
@dvdmn allredy memposting jawaban yang sama dua tahun lalu
NtFreX
2

Semua kredit untuk Frank Tzanabetis. Namun ini adalah contoh langsung terpendek, yang juga bertahan dalam kasus string kosong atau string JSON asli rusak:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

    ...
    private static string Format(string jsonString)
    {
        try
        {
            return JToken.Parse(jsonString).ToString(Formatting.Indented);
        }
        catch
        {
            return jsonString;
        }
    }
epox
sumber
2

Ini akan menempatkan setiap item di baris baru

VB.NET

mytext = responseFromServer.Replace("{", vbNewLine + "{")

C #

mytext = responseFromServer.Replace("{", Environment.NewLine + "{");
Andrew
sumber
1

Ini adalah varian dari jawaban yang diterima yang suka saya gunakan. Bagian yang dikomentari menghasilkan apa yang saya anggap sebagai format yang lebih mudah dibaca (Anda perlu mengomentari kode yang berdekatan untuk melihat perbedaannya):

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(@"\[\]", "[]");
    }
}
Harga J Bryan
sumber
1

Cukup gunakan JsonDocumentdan Utf8JsonWriter. Tidak diperlukan perpustakaan pihak ketiga. Tidak ada objek target untuk deserialisasi yang jsonStringdiperlukan.

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());
}
Roger.K
sumber
Menurut Anda JsonDocument.Parseapa? Tentunya itu membuat deserializes itu?
mpen
0

J Bryan Price, contoh yang bagus, tapi ada kekurangannya

{\"response\":[123, 456, {\"name\":\"John\"}, {\"count\":3}]}

setelah memformat

{
    "response" : [
        123,
         456,
         {
            "name" : "John"
        },
         {
            "count" : 3
        }
    ]
}

bias yang tidak tepat :(

Сергей Афоничев
sumber
0

Contoh

    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();
    }
Сергей Афоничев
sumber
0

Versi ini menghasilkan JSON yang lebih ringkas dan menurut saya lebih mudah dibaca karena Anda dapat melihat lebih banyak sekaligus. Ini dilakukan dengan memformat lapisan terdalam sebaris atau seperti struktur array yang ringkas.

Kode tersebut tidak memiliki ketergantungan tetapi lebih kompleks.

{ 
  "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} 
  ]
}

Kode berikut

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;
    }
}

Fungsi ini memungkinkan Anda menentukan titik awal untuk lekukan karena saya menggunakan ini sebagai bagian dari proses yang merakit file cadangan berformat JSON yang sangat besar.

AnthonyVO
sumber
0

Ini berfungsi untuk saya menggunakan System.Text.Json di .Net Core 3.1

 public string PrettyJson(string unPrettyJson)
 {
     var options = new JsonSerializerOptions(){
         WriteIndented = true
     };

     var jsonElement = JsonSerializer.Deserialize<JsonElement>(unPrettyJson);

     return JsonSerializer.Serialize(jsonElement, options);
 }
Gurdeep Singh Sidhu
sumber