如何在使用C#的.NET中获取格式化的JSON?

367

我正在使用 .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();
}

仅供参考:您实际上并没有使用“the” .NET JSON解析器,而是一个在旧的ASP.NET时代创建的旧解析器。今天也有新的System.Text.Json解析器,它更快,并且被认为是现在使用.NET的开箱即用解析器。JSON.NET也是另一个非常流行的.NET JSON库。 - Jim Aho
dvdmn的回答https://stackoverflow.com/questions/2661063/how-do-i-get-formatted-json-in-net-using-c/21407175#21407175不需要声明类型。 - undefined
19个回答

325

使用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输出的例子:http://james.newtonking.com/archive/2008/10/16/asp-net-mvc-and-json-net.aspx - Roman
15
@Brad 他展示了完全相同的代码,但是使用了一个模型。 - Mia
3
所以这个想法只是格式化。Indented。 - FindOutIslamNow
这种方法还可以避免制作JSON格式错误。 - leocrimson
这个简单的方法可行:private static string GetJson<T> (T json) { return JsonConvert.SerializeObject(json, Formatting.Indented); } - Andrés Fg

241

Json.Net库的简短示例代码

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

3
你实际上可以更进一步,创建一个扩展方法;将其设为 public 并将签名更改为 FormatJson(this string json)。 - bdwakefield
1
不需要扩展。 - Haseeb Mir
@HaseeBMir 很容易说,6.5年后,微软过去并没有那么关心开发者。 - dvdmn
1
注意:不需要将此转换为“dynamic parsedJson”。您可以使用“var”或“object”。 - Ron Sijm
@RonSijm 对象在某些情况下确实会抱怨属性。dynamic 不是必需的,但更安全。 - dvdmn
1
似乎这应该是被接受的答案。 - KWallace

157

如果您有一个 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();
        }
    }
}

7
相比其他方案,仅仅美化一个 Json 字符串,这是一个更加合适的解决方案。 - Jens Marchewka
2
以下用例将失败: JsonPrettify("null")JsonPrettify("\"string\"") - Liz Av
1
谢谢@Ekevoo,我已经回滚到之前的版本了! - Duncan Smart
@DuncanSmart 我喜欢这个版本!那个版本创建了更少的临时对象。我认为即使那些用例可行,这个版本也比我批评过的那个更好。 - Liz Av

138

使用JSON.net编辑现有JSON数据的最简短的美化版本:

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

5
即使事先不知道JSON的结构,这个方法仍然有效。而且这是这里最短的答案。 - foresightyj
3
这个方法可行,但仅限于JSON对象不是数组的情况。如果你知道它将是一个数组,你可以使用JArray.Parse来代替。 - Luke Z
6
好的,谢谢指出这个问题。我已经更新了我的答案,使用 JToken 替代 JObject。这样可以处理对象或数组,因为 JTokenJObjectJArray 的祖先类。 - asherber
2
非常感谢,伙计。我浪费了大约2个小时才找到这个解决方案... 想象不出我的生活没有@stackoverflow... - Rudresha Parameshappa
我真的更喜欢这个答案,因为代码短小而且有效。谢谢! - Marc Roussel
无法工作于: var json = @"{""dateMicrosoft"":""/Date(1526256000000)/""}";输出: { "dateMicrosoft": "2018-05-14T00:00:00Z" }目前还没有找到解决方法。 - Raj Rao

66

使用 Newtonsoft.Json.Linq 的单行代码:

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

1
我同意这是使用Newtonsoft格式化JSON的最简单API。 - Ethan Wu
2
在 Newtonsoft.Json 中找不到这个...可能是因为我使用的是旧版本。 - cslotty
5
它在NewtonSoft.Json.Linq命名空间中。我只知道这一点,因为我也曾搜索过它。 - Captain Kenpachi

51

Net Core 应用程序

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

4
这个答案应该有更多的投票。大家还在使用.NET Framework吗? - Tono Nam
这个答案是值得尊重的。 - petrosmm
这是最好的答案,使用标准的JSON库,没有复杂的代码。应该是最佳的回答。 - undefined

34

所有这些都可以在一行简单的代码中完成:

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

7
请记得添加 'using Newtonsoft.Json'。 - Ebube
最佳答案,我的朋友。 - RogerEdward

28

以下是使用微软的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());
}

1
这是一个很好的解决方案,适用于那些无法购买额外软件包的人。运行良好。 - Mark T
2
不错,我不想添加额外的包。 - SanjayD

23

2023 更新

对于那些想知道如何在 .NET 中使用 C# 获取格式化的 JSON 数据,并希望立即查看如何使用以及喜欢一行代码的人。以下是缩进的 JSON 字符串单行代码:

有两个知名的 JSON 格式化程序或解析器可以进行序列化:

Newtonsoft Json.Net 版本:

using Newtonsoft.Json;

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

.Net 7 版本:

using System.Text.Json;

var jsonString = JsonSerializer.Serialize(yourObj, new JsonSerializerOptions { WriteIndented = true });

3
这个应该得到更多的赞。 - Sevenate

13

您可以使用以下标准方法获取格式化的Json:

JsonReaderWriterFactory.CreateJsonWriter(Stream stream, Encoding encoding, bool ownsStream, bool indent, string indentChars)

只需将 "indent==true" 即可。

尝试类似这样的内容:

返回:

您可以使用以下标准方法获取格式化的Json:

JsonReaderWriterFactory.CreateJsonWriter(Stream stream, Encoding encoding, bool ownsStream, bool indent, string indentChars)

只需将 "indent==true" 即可。

尝试类似这样的内容:

    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 以避免在具有不同区域设置的计算机上反序列化时出现异常。例如,doubleDateTime 的无效格式有时会导致这种情况发生。

用于反序列化

    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转换都是文化不变的。 - Olexander Ivanitskyi
你好,我不确定XmlJsonWriter/Reader,但DataContractJsonSerializer使用Thread.CurrentThread.CurrentCulture。 当数据在A机器上序列化,但在B机器上反序列化时,可能会出现错误,因为它们具有不同的区域设置。 - Makeman
我在程序集System.Runtime.Serialization v.4.0.0.0中反编译了DataContractJsonSerializer,没有明确使用CurrentCulture。唯一使用文化的是基类XmlObjectSerializer中的CultureInfo.InvariantCulture,内部方法TryAddLineInfo - Olexander Ivanitskyi
所以,也许是我的错误。我稍后会检查它。可能我正在从另一个序列化程序的实现中推断这个文化问题。 - Makeman
1
我已经编辑了原始答案。看起来DataContract序列化程序是与文化无关的,但您应该注意避免使用其他类型的序列化程序进行序列化时出现特定于文化的错误。 :) - Makeman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接