web-dev-qa-db-ja.com

C#のJSONフォーマッター?

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

古いバージョンを更新しましたが、整数やブール値などの引用符で囲まれていない値をサポートするようになりました。

以前のバージョンをリファクタリングし、最終バージョンを取得しました。コードは短く、簡潔です。 1つの拡張メソッドのみが必要です。最も重要なのは、いくつかのバグを修正したことです。

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);
        }
    }
}
110
Peter Long

このために Newtonsoft.Json ライブラリを使用し、Formatting.Indented列挙型でSerializeObjectを呼び出すこともできます-

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

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ページにそのまま印刷することができます。

50
dvdmn

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
                    }
                }
            }
        }
    ]
}
32
Vince Panuccio

私が書いたもっと簡単なもの:

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

ここにはすでに Newtonsoft.JSON を使用するすばらしい回答がたくさんありますが、ToString()と組み合わせてJObject.Parseを使用するもう1つがあります。 :

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

独自の関数を記述する主な理由は、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();
            }
        }
    }
}
3
Max Venediktov

benjymous が指摘したように、一時オブジェクトで Newtonsoft.Json を使用し、デシリアライズ/シリアライズすることができます。

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

PrettyPrint()\rおよび\nをスキップする必要があります。いくつかのcrlfがすでに存在する(またはソースが既にフォーマットされている)ため、出力はおかしく見えます。

2
wvd_vegt

それを修正した...やや。

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
mpen

これにより、各アイテムが新しい行に配置されます

VB.NET

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

C#

mytext = responseFromServer.Replace("{", Environment.Newline + "{");
2
Andrew

クレジットはすべてフランク・ツァナベティスに帰属します。ただし、これは最も短い直接的な例であり、空の文字列または壊れた元の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
epox

これは、私が使用したい受け入れられている答えの変形です。コメントされた部分は、読みやすい形式と考えるものになります(違いを確認するには、隣接するコードをコメント化する必要があります)。

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
J Bryan Price

コンパクトな JSONフォーマッターVince 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
          }
        }
      }
    }
  ]
}
1
yallie

Jブライアンプライス、良い例ですが、欠点があります

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

フォーマット後

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

不適切なバイアス:(

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

このバージョンでは、一度に多くを表示できるため、よりコンパクトで読みやすい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形式のバックアップファイルをアセンブルするプロセスの一部としてこれを使用するため、インデントの開始点を指定できます。

0
AnthonyVO