รูปแบบ JSON ใน C #?


101

กำลังมองหาฟังก์ชันที่จะรับstringJson เป็นอินพุตและจัดรูปแบบด้วยตัวแบ่งบรรทัดและการเยื้อง การตรวจสอบความถูกต้องจะเป็นโบนัส แต่ไม่จำเป็นและฉันไม่จำเป็นต้องแยกวิเคราะห์เป็นวัตถุหรือสิ่งใด ๆ

มีใครรู้จักห้องสมุดแบบนี้บ้าง?


อินพุตตัวอย่าง:

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

คำตอบ:


2

สิ่งนี้ใช้ได้กับฉันโดยใช้ System.Text.Json ใน. 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);
 }

เย็น! ดูเหมือนว่าจะถูกเพิ่มเข้ามาใน. NET Core 3.0 จริงซึ่งเปิดตัวเมื่อวันที่ 23 กันยายน 2019
mpen

123

ฉันอัปเดตเวอร์ชันเก่าแล้วตอนนี้ควรรองรับค่าที่ไม่ได้ระบุไว้เช่นจำนวนเต็มและบูลีน

ฉันปรับโครงสร้างเวอร์ชันก่อนหน้าและรับเวอร์ชันสุดท้าย: โค้ดสั้นลงและสะอาดขึ้น ต้องใช้วิธีการขยายเพียงวิธีเดียว ที่สำคัญที่สุด: แก้ไขข้อบกพร่องบางอย่าง

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! คุณจะได้รับเช็ค
mpen

ของคุณก็ดีเช่นกันยกเว้นข้อบกพร่องเล็กน้อย: "url":"url('http://google.com')"ฟอร์แมตเป็น"url":"url('http : //google.com')". มีการเพิ่มช่องว่างก่อนและหลัง ":" ที่สองซึ่งไม่ถูกต้อง
Peter Long

อันนี้ทำงานกับค่าที่ไม่ได้ใส่เครื่องหมายเช่นจำนวนเต็มหรือไม่?
Johan Danforth

@JohanDanforth - เมื่อฉันลบบรรทัด # 64 (บิต "if (quoted)") ดูเหมือนว่าจะทำงานได้ดีสำหรับฉันด้วยค่าที่ไม่ได้ใส่เครื่องหมาย
jerhewet

ทำไมไม่ใช้.ToList()บนIEnumerableแทนการสร้างวิธีการใหม่? หากคุณใช้MoreLinqในโครงการของคุณสิ่งนี้จะรองรับการ.ForEach()ใช้งานIEnumerableนอกกรอบ
Kehlan Krumme

123

คุณยังสามารถใช้ไลบรารีNewtonsoft.Jsonสำหรับสิ่งนี้และเรียก SerializeObject พร้อมกับ FormattingIndented enum -

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

เอกสารประกอบ: ทำให้วัตถุเป็นอนุกรม


ปรับปรุง -

เพิ่งลองอีกครั้ง ค่อนข้างแน่ใจว่าสิ่งนี้เคยใช้งานได้จริง - บางทีอาจมีการเปลี่ยนแปลงในรุ่นต่อ ๆ ไปหรือบางทีฉันอาจจะจินตนาการถึงสิ่งต่างๆ อย่างไรก็ตามตามความคิดเห็นด้านล่างมันไม่ค่อยได้ผลเท่าที่ควร อย่างไรก็ตามสิ่งเหล่านี้ทำ (เพิ่งทดสอบใน linqpad) อันแรกมาจากความคิดเห็นอันที่ 2 เป็นตัวอย่างที่ฉันพบที่อื่นใน 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));
}

8
วินซ์ - จริง แต่ถ้าคุณจะทำเช่นนั้นอาจหมายความว่าคุณกำลังจะทำสิ่งอื่น ๆ ของ JSON ด้วยและถ้าเป็นเช่นนั้นก็สมเหตุสมผล แม้ว่าจะไม่ แต่ฉันก็ยืนยันว่ามันยังดีกว่าการหมุนเครื่องพิมพ์สวย ๆ ของคุณเองในกรณีส่วนใหญ่เนื่องจากการทำเช่นนั้นต้องใช้ความพยายามมากกว่า :)
Frank Tzanabetis

6
สิ่งนี้ใช้ไม่ได้ การต่อสตริงที่เป็น json อยู่แล้วด้วยวิธีนี้จะไม่ทำให้สวยงามแม้จะมีการจัดรูปแบบก็ตามระบุไว้ เพียงแค่ใส่เครื่องหมายคำพูดสตริงและหลีกเลี่ยงเครื่องหมายคำพูดที่มีอยู่ทั้งหมด
รอส

รอส - คุณลองแล้วหรือยัง? เมื่อคุณใช้ตัวเลือก Formatting.Indented กับมันมันจะ "พิมพ์สวย" สตริง JSON
Frank Tzanabetis

8
ฉันลองใช้รหัสด้านบนและได้ผลลัพธ์ (ผิด) เหมือนกัน - การแก้ไขสำหรับฉันคือ: var obj = JsonConvert.DeserializeObject(jsonString); var formatted = JsonConvert.SerializeObject(obj, Formatting.Indented) (เช่น deserialise เป็นวัตถุชั่วคราวจากนั้นกลับไปที่ json) - ไม่ใช่วิธีที่มีประสิทธิภาพมากที่สุด แต่อย่างน้อยก็ได้ผล!
benjymous

59

ตัวอย่างที่สั้นกว่าสำหรับไลบรารี 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
Rocklan

ทำงานได้ดีกับ newtonsoft.Json เวอร์ชัน 10.0.3 จัดรูปแบบไฟล์ JSON ขนาด 6MB ภายในเวลาไม่ถึง 5 วินาทีบน CPU Win10 Intel i7-7700 (4.20Ghz)
กระบอง

35

นี่คือเครื่องตกแต่ง 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
                    }
                }
            }
        }
    ]
}

2
เอาต์พุตจะปิด 1 ช่องว่างในทุกบรรทัดและอาจใช้ช่องว่างหลังเครื่องหมายทวิภาค
mpen

6
ฉันไม่อยากจะเชื่อเลยว่าคำตอบของ @Vince_Panucio มีเพียง 3 upvotes เท่านั้น? เป็นอัจฉริยะล้วนๆ ใช้รหัส linq ของเขาและวางลงใน visual studio จากนั้นใช้ resharper เพื่อแปลงเป็นห่วงโซ่วิธีการเพื่อดูว่าคุณเขียนสิ่งเดียวกันโดยใช้ปกติอย่างไรเลือก (x ... ) เลือก (y) และเป็นสองสาม หน้ายาว ทำได้ดีมากวินซ์ ... ทำได้ดีมาก!
snowcode

1
+1 สำหรับงานฝีมือที่ยอดเยี่ยม เท่าที่ฉันชอบสำหรับรหัสการผลิต (ที่ใช้ร่วมกัน) ฉันอาจจะแยกมันออกและแปลงเป็น foreach loop เพื่อประโยชน์ในการอ่าน / แก้จุดบกพร่อง
3dGrabber

1
@ เพิ่มช่องว่างพิเศษจะเป็นช่องว่างหลังเครื่องหมายจุลภาคและก่อนเครื่องหมายคำพูดในอินพุตต้นฉบับ ดังนั้นขึ้นอยู่กับการป้อนข้อมูลของคุณ ymmv
Jesse Chisholm

1
ไม่ใช่ผู้ชายที่สมบูรณ์แบบอย่าลังเลที่จะแก้ไขในโครงการของคุณเอง
Razor

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')"
Peter Long

6

มีคำตอบที่ยอดเยี่ยมมากมายที่ใช้Newtonsoft.JSON อยู่แล้ว แต่นี่เป็นอีกหนึ่งคำตอบที่ใช้JObject.Parseร่วมกับToString()เนื่องจากยังไม่ได้กล่าวถึง:

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

นี่ควรเป็นคำตอบ .. สองบรรทัดเท่านั้น แต่สำหรับกรณีที่ตัวแปรของคุณ json เป็น json-Object; การแยกวิเคราะห์อื่นอาจล้มเหลวหากอาร์กิวเมนต์เป็นอาร์เรย์สตริงค่าว่าง ฯลฯ
joedotnot

6

ผมรู้สึกประทับใจมากโดยมีขนาดกะทัดรัดจัดรูปแบบ JSONโดยวินซ์ Panuccio
นี่คือเวอร์ชันปรับปรุงที่ฉันใช้อยู่ตอนนี้:

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

3

เหตุผลหลักในการเขียนฟังก์ชันของคุณเองคือเฟรมเวิร์ก 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();
            }
        }
    }
}

2

คุณจำเป็นต้องข้าม\rและใน\n PrettyPrint()ผลลัพธ์ดูตลกที่มี crlf อยู่แล้ว (หรือมีการจัดรูปแบบแหล่งที่มาแล้ว)


2

แก้ไขมัน ...

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

เครดิต [ลิงก์ตาย]


2

ดังที่ระบุไว้ในbenjymousคุณสามารถใช้Newtonsoft.Jsonกับวัตถุชั่วคราวและ deserialize / serialize

var obj = JsonConvert.DeserializeObject(jsonString); 
var formatted = JsonConvert.SerializeObject(obj, Formatting.Indented);

1
@dvdmn allredy โพสต์ anwser เดียวกันเมื่อสองปีก่อน
NtFreX

2

เครดิตทั้งหมดเป็นของ Frank Tzanabetis อย่างไรก็ตามนี่เป็นตัวอย่างโดยตรงที่สั้นที่สุดซึ่งยังคงอยู่ในกรณีที่สตริงว่างเปล่าหรือสตริง JSON ดั้งเดิมที่ใช้งานไม่ได้:

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

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

2

การดำเนินการนี้จะทำให้แต่ละรายการขึ้นบรรทัดใหม่

VB.NET

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

ค#

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

1

นี่คือตัวแปรของคำตอบที่ยอมรับซึ่งฉันชอบใช้ ส่วนที่แสดงความคิดเห็นส่งผลให้สิ่งที่ฉันคิดว่าเป็นรูปแบบที่อ่านได้ง่ายขึ้น (คุณจะต้องแสดงความคิดเห็นเกี่ยวกับรหัสที่อยู่ติดกันเพื่อดูความแตกต่าง):

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(@"\[\]", "[]");
    }
}

1

เพียงใช้JsonDocumentและUtf8JsonWriter. ไม่จำเป็นต้องใช้ไลบรารีของบุคคลที่สาม ไม่มีวัตถุเป้าหมายสำหรับ deserialization สำหรับ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

0

J Bryan Price เป็นตัวอย่างที่ดี แต่มีข้อบกพร่อง

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

หลังจากการจัดรูปแบบ

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

อคติที่ไม่เหมาะสม :(


0

ตัวอย่าง

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

0

เวอร์ชันนี้สร้าง 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 ขนาดใหญ่มาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.