JSON форматер в C #? - PullRequest
       33

JSON форматер в C #?

87 голосов
/ 02 января 2011

Ищем функцию, которая примет string Json в качестве ввода и отформатирует его с разрывами строк и отступами. Проверка была бы бонусом, но не обязательна, и мне не нужно разбирать ее на объект или что-либо еще.

Кто-нибудь знает такую ​​библиотеку?


Пример ввода:

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

Ответы [ 16 ]

107 голосов
/ 04 июня 2011

Я обновил старую версию, теперь она должна поддерживать значения без кавычек, такие как целые и логические значения.

Я провел рефакторинг предыдущей версии и получил финальную версию: Код короче и чище. Требуется только один метод расширения. Самое важное: исправлено несколько ошибок.

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);
        }
    }
}
102 голосов
/ 23 октября 2013

Вы также можете использовать для этого библиотеку Newtonsoft.Json и вызвать SerializeObject с перечислением Formatting.Indented -

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

Документация: Сериализация объекта


Обновление -

Просто попробовал еще раз.Уверен, раньше это работало - возможно, это изменилось в следующей версии, или, может быть, я просто представляю себе что-то.Во всяком случае, согласно комментариям ниже, это не совсем работает, как ожидалось.Однако это так (только что протестировано в linqpad).Первый из комментариев, второй - пример, который я нашел в других местах 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));
}
51 голосов
/ 28 января 2014

Сокращенный пример для библиотеки 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-странице.

30 голосов
/ 16 июля 2014

Вот компактная версия украшения 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
                    }
                }
            }
        }
    ]
}
8 голосов
/ 03 января 2011

Еще проще, что я только что написал:

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
                    }
                }
            }
        }
    ]
}
4 голосов
/ 07 марта 2018

Здесь уже есть куча хороших ответов, которые используют Newtonsoft.JSON , но вот еще один, который использует JObject.Parse в сочетании с ToString(), поскольку об этом еще не упоминалось:

var jObj = Newtonsoft.Json.Linq.JObject.Parse(json);
var formatted = jObj.ToString(Newtonsoft.Json.Formatting.Indented);
2 голосов
/ 30 марта 2017

Все кредиты принадлежат Фрэнку Цанабетису.Однако это самый короткий прямой пример, который также сохраняется в случае пустой строки или неверной исходной строки 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 голосов
/ 06 февраля 2017

Основная причина написания вашей собственной функции заключается в том, что платформы 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 голосов
/ 12 сентября 2016

Это поместит каждый элемент в новую строку

VB.NET

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

C #

mytext = responseFromServer.Replace("{", Environment.Newline + "{");
2 голосов
/ 14 июля 2016

Как указано benjymous , вы можете использовать Newtonsoft.Json с временным объектом и десериализовать / сериализовать.

var obj = JsonConvert.DeserializeObject(jsonString); 
var formatted = JsonConvert.SerializeObject(obj, Formatting.Indented);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...