Jak uzyskać sformatowany JSON w .NET przy użyciu C #?


256

Używam parsera .NET JSON i chciałbym serializować mój plik konfiguracyjny, aby był czytelny. Więc zamiast:

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

Chciałbym coś ładniejszego:

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

Mój kod jest mniej więcej taki:

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

Odpowiedzi:


257

Trudno będzie ci to osiągnąć za pomocą JavaScriptSerializer.

Wypróbuj JSON.Net .

Z drobnymi modyfikacjami z przykładu 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; }
    }
}

Wyniki

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

Dokumentacja: Serializacja obiektu


Istnieje również przykład formatowania danych wyjściowych Jsona
R0MANARMY

15
@Brad Pokazał absolutnie ten sam kod, ale używając modelu
Mia

Więc pomysł jest po prostu Formatowanie.Indentowane
FindOutIslamNow

Ta metoda pozwala także uniknąć błędów w formacie JSON.
Anshuman Goel

172

Krótszy przykładowy kod dla biblioteki Json.Net

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

1
Możesz faktycznie pójść o krok dalej i stworzyć metodę rozszerzenia; upublicznij i zmień podpis na FormatJson (ten ciąg json)
bdwakefield

129

Jeśli masz ciąg JSON i chcesz go „upiększać”, ale nie chcesz serializować go do znanego typu C #, a następnie wykonaj poniższe czynności (używają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
Jeśli tylko upiększysz łańcuch Jsona, jest to znacznie poprawniejsze rozwiązanie niż inne ...
Jens Marchewka

2
Następujące przypadki użycia zakończą się niepowodzeniem: JsonPrettify("null")orazJsonPrettify("\"string\"")
Ekevoo,

1
Dzięki @Ekevoo, przywróciłem go do poprzedniej wersji!
Duncan Smart

@DuncanSmart Uwielbiam to! Ta wersja tworzy znacznie mniej obiektów tymczasowych. Myślę, że jest lepszy niż ten, który skrytykowałem, nawet jeśli te przypadki użycia zadziałały.
Ekevoo,

97

Najkrótsza wersja dla upiększenia istniejącego JSON: (edycja: za pomocą JSON.net)

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

Wejście:

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

Wynik:

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

Aby ładnie wydrukować obiekt:

JToken.FromObject(myObject).ToString()

4
Działa to nawet bez uprzedniej znajomości struktury JSON. I to jest najkrótsza odpowiedź tutaj。
foresightyj

1
Działa to, ale tylko wtedy, gdy obiekt json nie jest tablicą. Jeśli wiesz, że będzie to tablica, możesz zamiast tego użyć JArray.Parse.
Luke Z

3
Ach, dobra racja, dzięki. Zaktualizowałem moją odpowiedź do użycia JTokenzamiast JObject. Działa to z obiektami lub tablicami, ponieważ JTokenjest to klasa przodków zarówno dla, jak JObjecti JArray.
asherber

Wielkie dzięki, człowieku, zmarnowałem około 2 godzin, aby dostać się do tego rozwiązania ... Nie wyobrażam sobie życia bez @stackoverflow ...
Rudresha Parameshappa

Naprawdę wolę ten od innych odpowiedzi. Krótki kod i skuteczne. Dziękuję
Marc Roussel

47

Oneliner używa Newtonsoft.Json.Linq:

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

Zgadzam się, że jest to najprostszy interfejs API do formatowania JSON przy użyciu Newtonsoft
Ethan Wu

2
Nie mogłem tego znaleźć w Newtonsoft.Json ... może mam starszą wersję.
cslotty

2
Jest w przestrzeni nazw NewtonSoft.Json.Linq. Wiem to tylko dlatego, że też tego szukałem.
Kapitan Kenpachi,

12

Możesz użyć następującej standardowej metody uzyskania sformatowanego Jsona

JsonReaderWriterFactory.CreateJsonWriter (strumień strumieniowy, kodowanie, bool ownsStream, wcięcie bool, wcięcie ciąguChars)

Ustaw tylko „wcięcie == prawda”

Spróbuj czegoś takiego

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

Zwróć uwagę na linie

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

W przypadku niektórych rodzajów serializatorów XML należy użyć InvariantCulture, aby uniknąć wyjątku podczas deserializacji na komputerach z różnymi ustawieniami regionalnymi. Na przykład niepoprawny format double lub DateTime czasami powoduje je .

Za deserializację

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

Dzięki!


Cześć, @Makeman, czy kiedykolwiek odtworzyłeś błędy serializacji spowodowane przez różne kultury? Wydaje się, że wszystkie konwersje XmlJsonWriter / Reader są niezmienne w kulturze.
Oleksander Iwanicki

Witaj, nie jestem pewien co do XmlJsonWriter / Reader, ale DataContractJsonSerializer używa Thread.CurrentThread.CurrentCulture. Błędy mogą wystąpić, gdy dane zostały zserializowane na komputerze A, ale zserializowane na B z innymi ustawieniami regionalnymi.
Makeman

Dekompilowałem DataContractJsonSerializerw asemblerze System.Runtime.Serialization v.4.0.0.0, nie ma wyraźnego użycia CurrentCulture. Jedynym zastosowaniem kultury jest CultureInfo.InvariantCultureklasa podstawowa XmlObjectSerializer, metoda wewnętrzna TryAddLineInfo.
Olexander Ivanitskyi

Więc może to mój błąd. Sprawdzę to później. Możliwe, że ekstrapoluję ten problem kulturowy z implementacji innego serializatora.
Makeman

1
Zredagowałem oryginalną odpowiedź. Wydaje się, że serializatory DataContract są niezależne od kultury, ale należy zwrócić uwagę, aby uniknąć błędów specyficznych dla kultury podczas serializacji przez inny rodzaj serializatorów. :)
Makeman

6

Wszystko to można zrobić w jednym prostym wierszu:

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

1
Pamiętaj, aby dodać „using Newtonsoft.Json”
Ebube,

najlepsza odpowiedź, przyjacielu.
RogerEdward

5

Oto rozwiązanie wykorzystujące bibliotekę 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());
}

To dobre rozwiązanie dla tych, którzy nie mogą kupić dodatkowego pakietu. Działa dobrze.
Mark T

2

Najpierw chciałem dodać komentarz pod postem Duncan Smart, ale niestety nie mam jeszcze wystarczającej reputacji, aby zostawić komentarz. Spróbuję tutaj.

Chcę tylko ostrzec o skutkach ubocznych.

JsonTextReader wewnętrznie analizuje json na wpisane JTokens, a następnie serializuje je z powrotem.

Na przykład, jeśli oryginalny JSON był

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

Po upiększeniu dostaniesz

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

Oczywiście oba ciągi JSON są równoważne i deserializują je do obiektów o równej strukturze, ale jeśli chcesz zachować oryginalne wartości ciągów, musisz wziąć to pod uwagę


Świetna dyskusja na temat tego szczegółu tutaj ... github.com/JamesNK/Newtonsoft.Json/issues/862 Ciekawe, jak ewoluował ten szczegół. Nauczyłem się czegoś nowego o moim głównym parserze json - dziękuję za komentarz.
Sql Surfer

2

Za pomocą System.Text.Jsonzestawu 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

To zadziałało dla mnie. W przypadku, gdy ktoś szuka wersji 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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.