Как получить отформатированный JSON в .NET с помощью C #?


256

Я использую анализатор .NET JSON и хотел бы сериализовать мой конфигурационный файл, чтобы он был читабельным. Так что вместо:

{"blah":"v", "blah2":"v2"}

Я хотел бы что-то более хорошее, как:

{
    "blah":"v", 
    "blah2":"v2"
}

Мой код выглядит примерно так:

using System.Web.Script.Serialization; 

var ser = new JavaScriptSerializer();
configSz = ser.Serialize(config);
using (var f = (TextWriter)File.CreateText(configFn))
{
    f.WriteLine(configSz);
    f.Close();
}

Ответы:


257

Вам будет трудно это сделать с помощью JavaScriptSerializer.

Попробуйте JSON.Net .

С небольшими изменениями из примера JSON.Net

using System;
using Newtonsoft.Json;

namespace JsonPrettyPrint
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Product product = new Product
                {
                    Name = "Apple",
                    Expiry = new DateTime(2008, 12, 28),
                    Price = 3.99M,
                    Sizes = new[] { "Small", "Medium", "Large" }
                };

            string json = JsonConvert.SerializeObject(product, Formatting.Indented);
            Console.WriteLine(json);

            Product deserializedProduct = JsonConvert.DeserializeObject<Product>(json);
        }
    }

    internal class Product
    {
        public String[] Sizes { get; set; }
        public decimal Price { get; set; }
        public DateTime Expiry { get; set; }
        public string Name { get; set; }
    }
}

Полученные результаты

{
  "Sizes": [
    "Small",
    "Medium",
    "Large"
  ],
  "Price": 3.99,
  "Expiry": "\/Date(1230447600000-0700)\/",
  "Name": "Apple"
}

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


Есть также пример форматирования вывода json в его блоге james.newtonking.com/archive/2008/10/16/…
R0MANARMY

15
@Brad Он показал абсолютно тот же код, но с использованием модели
Mia

Так что идея - это просто Форматирование. С
отступом

Этот метод также избавляет от ошибок формата JSON.
Аншуман Гоэль

173

Более короткий пример кода для библиотеки Json.Net

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

1
Вы можете сделать этот шаг дальше и создать метод расширения; сделайте его публичным и измените подпись на FormatJson (эта строка json)
bdwakefield

129

Если у вас есть строка JSON и вы хотите ее «предварительно преобразовать», но не хотите сериализовать ее в известный тип C # и из него, то сработает следующее (с использованием JSON.NET):

using System;
using System.IO;
using Newtonsoft.Json;

class JsonUtil
{
    public static string JsonPrettify(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();
        }
    }
}

6
Только для предварительной настройки строки Json это гораздо правильнее, чем другие ...
Jens Marchewka

2
Следующие варианты использования не удастся: JsonPrettify("null")иJsonPrettify("\"string\"")
Ekevoo

1
Спасибо @Ekevoo, я откатился на предыдущую версию!
Дункан Смарт

@DuncanSmart Я люблю это! Эта версия создает гораздо меньше временных объектов. Я думаю, что это лучше, чем тот, который я критиковал, даже если эти варианты использования работали.
Ekevoo

97

Кратчайшая версия для предварительного кодирования существующего JSON: (редактировать: используя JSON.net)

JToken.Parse("mystring").ToString()

Входные данные:

{"menu": { "id": "file", "value": "File", "popup": { "menuitem": [ {"value": "New", "onclick": "CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, {"value": "Close", "onclick": "CloseDoc()"} ] } }}

Вывод:

{
  "menu": {
    "id": "file",
    "value": "File",
    "popup": {
      "menuitem": [
        {
          "value": "New",
          "onclick": "CreateNewDoc()"
        },
        {
          "value": "Open",
          "onclick": "OpenDoc()"
        },
        {
          "value": "Close",
          "onclick": "CloseDoc()"
        }
      ]
    }
  }
}

Чтобы красиво распечатать объект:

JToken.FromObject(myObject).ToString()

4
Это работает, даже не зная структуры JSON заранее. И это самый короткий ответ здесь
fore

1
Это работает, но только если объект json не является массивом. Если вы знаете, что это будет массив, вы можете использовать вместо него JArray.Parse.
Люк Z

3
Ах, хорошая мысль, спасибо. Я обновил свой ответ, чтобы использовать JTokenвместо JObject. Это работает с объектами или массивами, так как JTokenявляется классом предка для обоих JObjectи JArray.
Ашербер

Большое спасибо, парень, которого я потратил около 2 часов, чтобы найти это решение ... Не могу представить свою жизнь без @stackoverflow ...
Rudresha Parameshappa

Я действительно предпочитаю этот один из других ответов. Короткий код и эффективный. Спасибо
Марк Руссель

47

Oneliner используя Newtonsoft.Json.Linq:

string prettyJson = JToken.Parse(uglyJsonString).ToString(Formatting.Indented);

Я согласен, что это самый простой API для форматирования JSON с использованием Newtonsoft
Итан Ву,

2
Не могу найти это в Newtonsoft.Json ... возможно у меня есть более старая версия.
cslotty

2
Он находится в пространстве имен NewtonSoft.Json.Linq. Я знаю это только потому, что тоже искал это.
Капитан Кенпачи

12

Вы можете использовать следующий стандартный метод для получения отформатированного Json

JsonReaderWriterFactory.CreateJsonWriter (Поток потока, кодировка кодирования, bool ownsStream, отступ bool, строка indentChars)

Только установить "отступ == правда"

Попробуйте что-то вроде этого

    public readonly DataContractJsonSerializerSettings Settings = 
            new DataContractJsonSerializerSettings
            { UseSimpleDictionaryFormat = true };

    public void Keep<TValue>(TValue item, string path)
    {
        try
        {
            using (var stream = File.Open(path, FileMode.Create))
            {
                //var currentCulture = Thread.CurrentThread.CurrentCulture;
                //Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

                try
                {
                    using (var writer = JsonReaderWriterFactory.CreateJsonWriter(
                        stream, Encoding.UTF8, true, true, "  "))
                    {
                        var serializer = new DataContractJsonSerializer(type, Settings);
                        serializer.WriteObject(writer, item);
                        writer.Flush();
                    }
                }
                catch (Exception exception)
                {
                    Debug.WriteLine(exception.ToString());
                }
                finally
                {
                    //Thread.CurrentThread.CurrentCulture = currentCulture;
                }
            }
        }
        catch (Exception exception)
        {
            Debug.WriteLine(exception.ToString());
        }
    }

Обратите внимание на линии

    var currentCulture = Thread.CurrentThread.CurrentCulture;
    Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
    ....
    Thread.CurrentThread.CurrentCulture = currentCulture;

Для некоторых видов xml-сериализаторов следует использовать InvariantCulture, чтобы избежать исключений при десериализации на компьютерах с различными региональными настройками. Например, неправильный формат double или DateTime иногда вызывают их.

Для десериализации

    public TValue Revive<TValue>(string path, params object[] constructorArgs)
    {
        try
        {
            using (var stream = File.OpenRead(path))
            {
                //var currentCulture = Thread.CurrentThread.CurrentCulture;
                //Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

                try
                {
                    var serializer = new DataContractJsonSerializer(type, Settings);
                    var item = (TValue) serializer.ReadObject(stream);
                    if (Equals(item, null)) throw new Exception();
                    return item;
                }
                catch (Exception exception)
                {
                    Debug.WriteLine(exception.ToString());
                    return (TValue) Activator.CreateInstance(type, constructorArgs);
                }
                finally
                {
                    //Thread.CurrentThread.CurrentCulture = currentCulture;
                }
            }
        }
        catch
        {
            return (TValue) Activator.CreateInstance(typeof (TValue), constructorArgs);
        }
    }

Спасибо!


Привет, @Makeman, ты когда-нибудь воспроизводил ошибки сериализации, вызванные разными культурами? Похоже, что преобразования XmlJsonWriter / Reader являются инвариантами культуры.
Александр Иваницкий

Здравствуйте, я не уверен насчет XmlJsonWriter / Reader, но DataContractJsonSerializer использует Thread.CurrentThread.CurrentCulture. Ошибки могут возникать, когда данные были сериализованы на машине A, но десериализованы на B с другими региональными настройками.
Makeman

Я декомпилировал DataContractJsonSerializerв сборке System.Runtime.Serialization v.4.0.0.0, нет явного использования CurrentCulture. Единственное использование культуры CultureInfo.InvariantCultureв базовом классе XmlObjectSerializer, внутренний метод TryAddLineInfo.
Александр Иваницкий

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

1
Я отредактировал оригинальный ответ. Кажется, что сериализаторы DataContract не зависят от культуры, но вам следует уделить внимание тому, чтобы избежать ошибок, специфичных для культуры, во время сериализации другими типами сериализаторов. :)
Makeman

6

Все это можно сделать в одну простую строку:

string jsonString = JsonConvert.SerializeObject(yourObject, Formatting.Indented);

1
Не забудьте добавить 'using Newtonsoft.Json'
Ebube

лучший ответ мой друг.
RogerEdward

5

Вот решение, использующее библиотеку Microsoft System.Text.Json :

static string FormatJsonText(string jsonString)
{
    using var doc = JsonDocument.Parse(
        jsonString,
        new JsonDocumentOptions
        {
            AllowTrailingCommas = true
        }
    );
    MemoryStream memoryStream = new MemoryStream();
    using (
        var utf8JsonWriter = new Utf8JsonWriter(
            memoryStream,
            new JsonWriterOptions
            {
                Indented = true
            }
        )
    )
    {
        doc.WriteTo(utf8JsonWriter);
    }
    return new System.Text.UTF8Encoding()
        .GetString(memoryStream.ToArray());
}

Это хорошее решение для тех, кто не может приобрести дополнительный пакет. Работает хорошо.
Марк Т

2

Сначала я хотел добавить комментарий под постом Дункан Смарт, но, к сожалению, у меня пока недостаточно репутации, чтобы оставлять комментарии. Поэтому я попробую это здесь.

Я просто хочу предупредить о побочных эффектах.

JsonTextReader внутренне анализирует json в типизированные JTokens и затем сериализует их обратно.

Например, если ваш оригинальный JSON был

 { "double":0.00002, "date":"\/Date(1198908717056)\/"}

После преттификации вы получаете

{ 
    "double":2E-05,
    "date": "2007-12-29T06:11:57.056Z"
}

Конечно, обе строки json эквивалентны и будут десериализованы в структурно равные объекты, но если вам нужно сохранить исходные строковые значения, вам нужно принять это к сведению


Здесь подробно обсуждают эту деталь ... github.com/JamesNK/Newtonsoft.Json/issues/862 Интересно, как эта деталь развивалась. Я узнал кое-что новое о моем первичном парсере json - Спасибо за ваш комментарий.
Sql Surfer

2

Используя System.Text.Jsonнабор JsonSerializerOptions.WriteIndented = true:

JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize<Type>(object, options);

2

netcoreapp3.1

var js = JsonSerializer.Serialize(obj, new JsonSerializerOptions {
             WriteIndented = true
         });

0

Это сработало для меня. В случае, если кто-то ищет версию VB.NET.

@imports System
@imports System.IO
@imports Newtonsoft.Json

Public Shared Function JsonPrettify(ByVal json As String) As String
  Using stringReader = New StringReader(json)

    Using stringWriter = New StringWriter()
      Dim jsonReader = New JsonTextReader(stringReader)
      Dim jsonWriter = New JsonTextWriter(stringWriter) With {
          .Formatting = Formatting.Indented
      }
      jsonWriter.WriteToken(jsonReader)
      Return stringWriter.ToString()
    End Using
  End Using
End Function

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.