Json和Xml序列化,哪个性能更好?

17

我需要将一些配置信息存储到文件中。在C#代码中,配置数据由类表示,在文件中我将以json或xml格式保存此类数据。那么,序列化json或xml的性能哪个更好?


请查看以下帖子,它涉及JSON。SO LNK - Rajesh Subramanian
1
不如问什么是最合适的,而不是问什么是最快的?XML配置文件在.NET世界中是一个公认的标准。在这种情况下,我会遵循“最少惊讶原则”(即使用XML)。 - Adam Houldsworth
3
我不同意所有认为JSON不易读的答案。恰恰相反,J代表Javascript。在JSON问世之前,它就被设计成可以手动输入和人类可读。XML无法做到这一点,而且它更难读懂。但无论如何,找到最佳性能很容易。只需编写JSON和XML格式的配置文件,然后编写一个序列化每个文件的函数。使用StopWatch类测量执行1000次迭代所需的时间即可知道哪个执行效果更好。 - Wedge
虽然JSON与XML的问题可能会对性能产生一些影响,但解析器的实现可能更为相关。如果您真的关心性能,您可能不应该使用JSON或XML,也许您需要像https://learn.microsoft.com/en-us/aspnet/core/grpc/?view=aspnetcore-6.0这样的东西。这是一个愚蠢的问题,只能用愚蠢的答案来回答。 - Jodrell
6个回答

35

我有答案,不用再猜了。这是测试程序:

class Program
{
    static void Main(string[] args)
    {
        string xmlConfig = "";
        string jsonConfig = "";

        Config myConfig = new Config()
        {
            value = "My String Value",
            DateStamp = DateTime.Today,
            counter = 42,
            Id = Guid.NewGuid()
        };

        // Make both strings
        DataContractSerializer xmlSerializer = new DataContractSerializer(typeof(Config));
        using (MemoryStream xmlStream = new MemoryStream())
        {
            xmlSerializer.WriteObject(xmlStream, myConfig);
            xmlConfig = Encoding.UTF8.GetString(xmlStream.ToArray());
        }

        DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(Config));
        using (MemoryStream jsonStream = new MemoryStream())
        {
            jsonSerializer.WriteObject(jsonStream, myConfig);
            jsonConfig = Encoding.UTF8.GetString(jsonStream.ToArray());
        }

        // Test Single
        var XmlSingleTimer = Stopwatch.StartNew();
        SerializeXML(xmlConfig, 1);
        XmlSingleTimer.Stop();

        var JsonSingleTimer = Stopwatch.StartNew();
        SerializeJSON(jsonConfig, 1);
        JsonSingleTimer.Stop();

        // Test 1000
        var XmlTimer = Stopwatch.StartNew();
        SerializeXML(xmlConfig, 1000);
        XmlTimer.Stop();

        var JsonTimer = Stopwatch.StartNew();
        SerializeJSON(jsonConfig, 1000);
        JsonTimer.Stop();

        // Test 10000
        var XmlTimer2 = Stopwatch.StartNew();
        SerializeXML(xmlConfig, 10000);
        XmlTimer2.Stop();

        var JsonTimer2 = Stopwatch.StartNew();
            SerializeJSON(jsonConfig, 10000);
        JsonTimer2.Stop();

        Console.WriteLine(String.Format("XML Serialization Single: {0}ms", XmlSingleTimer.Elapsed.TotalMilliseconds));
        Console.WriteLine(String.Format("JSON Serialization Single: {0}ms", JsonSingleTimer.Elapsed.TotalMilliseconds));
        Console.WriteLine();
        Console.WriteLine(String.Format("XML Serialization 1000: {0}ms", XmlTimer.Elapsed.TotalMilliseconds));
        Console.WriteLine(String.Format("JSON Serialization 1000: {0}ms ", JsonTimer.Elapsed.TotalMilliseconds));
        Console.WriteLine();
        Console.WriteLine(String.Format("XML Serialization 10000: {0}ms ", XmlTimer2.ElapsedMilliseconds));
        Console.WriteLine(String.Format("JSON Serialization 10000: {0}ms ", JsonTimer2.ElapsedMilliseconds));
    }

    public static void SerializeXML(string xml, int iterations)
    {
        DataContractSerializer xmlSerializer = new DataContractSerializer(typeof(Config));
        for (int i = 0; i < iterations; i++)
        {
            using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
            {
                Config serialized = (Config)xmlSerializer.ReadObject(stream);
            }
        }
    }

    public static void SerializeJSON(string json, int iterations)
    {
        DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(Config));
        for (int i = 0; i < iterations; i++)
        {
            using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
            {
                Config serialized = (Config)jsonSerializer.ReadObject(stream);
            }
        }
    }
}

public class Config
{
    public string value;
    public DateTime DateStamp;
    public int counter;
    public Guid Id;
}

这是测量输出结果:

XML Serialization Single: 2.3764ms
JSON Serialization Single: 2.1432ms

XML Serialization 1000: 13.7754ms
JSON Serialization 1000: 13.747ms

XML Serialization 10000: 100ms
JSON Serialization 10000: 134ms

在1次迭代后,JSON的速度始终比XML稍快。经过1000次迭代后,它们之间几乎没有区别。但是,在10000次迭代后,XML明显更快。

目前还无法解释为什么一次一次地解析JSON会更快,但是如果重复解析,则XML会更快。可能是由于缓存或库中的某些花哨功能造成的。您可以看到JsonSerializer以线性方式扩展,将迭代次数增加一个数量级就会线性地增加已用时间一个数量级。但XmlSerializer的表现不同,其性能并没有呈线性扩展。

我多次重复此操作,并始终得到相同的结果。

因此,教训是:如果您只需要解析单个对象一次,则JSON会略微更好。但是,如果您要重复解析对象,则XML可能表现更佳。尽管我没有测试对象值在每次迭代中更改时会发生什么,但那可能会有所不同。

还请注意,我在此处使用的是本机Runtime.Serialization库。其他库可能会产生不同的结果。

编辑:我刚刚尝试了一下,在调用字符串时每次都生成一个新的Guid和随机整数。对于单次迭代和10000次迭代测试,这对结果没有影响。但是对于1000次迭代,JSON大约快了1毫秒。因此,看起来XML序列化程序确实正在缓存值。


8
ه¦‚و‍œن½؟用Newtonsoft.Jsonè؟›è،ŒJSONه؛ڈهˆ—هŒ–,那ه°±ه¤ھو£’ن؛†.. :) - TryingToImprove
9
我提供了所有的代码,你可以使用任何你想要的序列化程序来尝试它;) - Wedge
我现在添加了一个答案,它还测试了Newtonsoft.Json。基准测试结果显示,在超过1次迭代的情况下,Newtonsoft.Json在处理XML和JSON的速度上要比原生的DataContractSerializers快得多。 - Dan Atkinson

8
当我查找.NET应用程序中的配置时,我期望在某个名为MyApp.exe.config的XML文件中找到它。
遵循最少惊讶原则,我更喜欢使用XML序列化而不是JSON。另外一个好处是XML格式的配置可以适应Configuration API。除此之外,两者都有相同的支持:跨平台、良好的解析器、基于文本等。
性能只有成为问题时才是问题。我喜欢在编码之前识别潜在的问题,但这通常是针对由架构决策引入的性能问题。像这样小而相对独立的东西,如果在分析中证明存在问题,则更改起来不会很困难。

4

这是对@Wedge在2014年回答的更新。

它是用.NET 6.0编写的,同时还使用了流行的Newtonsoft.Json库。我还增加了迭代次数。之前是1、1,000和10,000。这个版本现在也进行100,000、1,000,000和10,000,000次迭代,但仍然非常不科学,也不现实。

下面是使用的代码。如果您想复制此代码,请确保导入Newtonsoft.Json到您的项目中。

using Newtonsoft.Json;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;

Config myConfig = new Config()
{
    value = "My String Value",
    DateStamp = DateTime.Today,
    counter = 42,
    Id = Guid.NewGuid()
};

// Xml serializer
string xmlConfig = "";
var xmlSerializer = new DataContractSerializer(typeof(Config));
using (var xmlStream = new MemoryStream())
{
    xmlSerializer.WriteObject(xmlStream, myConfig);
    xmlConfig = Encoding.UTF8.GetString(xmlStream.ToArray());
}

// Json serializer
string jsonConfig = "";
var jsonSerializer = new DataContractJsonSerializer(typeof(Config));
using (var jsonStream = new MemoryStream())
{
    jsonSerializer.WriteObject(jsonStream, myConfig);
    jsonConfig = Encoding.UTF8.GetString(jsonStream.ToArray());
}

// Newtonsoft.Json serializer.
string newtonsoftJsonConfig = "";
var newtonsoftJsonSerializer = new JsonSerializer();
using (var newtonSoftMemoryStream = new MemoryStream())
using (var writer = new StreamWriter(newtonSoftMemoryStream))
using (var newtonsoftJsonWriter = new JsonTextWriter(writer))
{
    newtonsoftJsonSerializer.Serialize(newtonsoftJsonWriter, myConfig);
    newtonsoftJsonWriter.Flush();
    newtonSoftMemoryStream.Position = 0;
    newtonsoftJsonConfig = Encoding.UTF8.GetString(newtonSoftMemoryStream.ToArray());
}

// Create a group of 5 different tests.
int[] counterArray = { 1, 1000, 10000, 100000, 1000000, 10000000 };
foreach (var iterations in counterArray)
{
    // Serialize XML.
    var xmlTimer = Stopwatch.StartNew();
    SerializeXML(xmlConfig, iterations);
    xmlTimer.Stop();

    // Serialize JSON.
    var jsonTimer = Stopwatch.StartNew();
    SerializeJSON(jsonConfig, iterations);
    jsonTimer.Stop();

    // Serialize JSON (Newtonsoft).
    var newtonsoftJsonTimer = Stopwatch.StartNew();
    SerializeNewtonsoftJson(newtonsoftJsonConfig, iterations);
    newtonsoftJsonTimer.Stop();

    Console.WriteLine($"XML Serialization {iterations}: {xmlTimer.Elapsed.TotalMilliseconds}ms");
    Console.WriteLine($"JSON Serialization {iterations}: {jsonTimer.Elapsed.TotalMilliseconds}ms");
    Console.WriteLine($"Newtonsoft.Json Serialization {iterations}: {newtonsoftJsonTimer.Elapsed.TotalMilliseconds}ms");
    Console.WriteLine();
}

static void SerializeXML(string xml, int iterations)
{
    var xmlSerializer = new DataContractSerializer(typeof(Config));
    for (var i = 0; i < iterations; i++)
    {
        using var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
        var serialized = (Config)xmlSerializer.ReadObject(stream);
    }
}

static void SerializeJSON(string json, int iterations)
{
    var jsonSerializer = new DataContractJsonSerializer(typeof(Config));
    for (var i = 0; i < iterations; i++)
    {
        using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
        var serialized = (Config)jsonSerializer.ReadObject(stream);
    }
}

static void SerializeNewtonsoftJson(string json, int iterations)
{
    // Newtonsoft.Json serializer.
    var newtonsoftJsonSerializer = new JsonSerializer();
    for (var i = 0; i < iterations; i++)
    {
        using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
        using var reader = new JsonTextReader(new StreamReader(stream, new UTF8Encoding()));
        var serialized = newtonsoftJsonSerializer.Deserialize<Config>(reader);
    }
}

public class Config
{
    public string value;
    public DateTime DateStamp;
    public int counter;
    public Guid Id;
}

以下是基准测试结果:

XML Serialization 1: 4.3958ms
JSON Serialization 1: 3.3516ms
Newtonsoft.Json Serialization 1: 37.5018ms

XML Serialization 1000: 11.137ms
JSON Serialization 1000: 6.8425ms
Newtonsoft.Json Serialization 1000: 2.4205ms

XML Serialization 10000: 39.1409ms
JSON Serialization 10000: 56.8301ms
Newtonsoft.Json Serialization 10000: 21.352ms

XML Serialization 100000: 358.903ms
JSON Serialization 100000: 519.5981ms
Newtonsoft.Json Serialization 100000: 197.7238ms

XML Serialization 1000000: 3585.8248ms
JSON Serialization 1000000: 5256.336ms
Newtonsoft.Json Serialization 1000000: 2006.7546ms

XML Serialization 10000000: 36326.6082ms
JSON Serialization 10000000: 53260.1445ms
Newtonsoft.Json Serialization 10000000: 20506.9946ms

正如您所看到的,在Newtonsoft.Json中进行单个序列化非常缓慢-几乎慢了10倍。 然而,在多次迭代中,这个数字显著下降,Newtonsoft.Json成为明显的赢家。

同样,这不是科学的,也不是现实的,但确实给出了使用Newtonsoft.Json进行序列化性能的一些想法。

如果有人想指出我的答案问题,提供改进其中任何一个的性能建议,请让我知道。


我不是专家,所以不知道如何“改进”你的答案,但我注意到你提到单次迭代速度较慢,我想指出这并不是事实,可以参考答案 - Barreto
@BarretoFreekhealer 第一次迭代很慢,因为.NET需要构建该类型的序列化。一旦完成,它将被应用程序缓存,以便后续重复操作更快。你观察到的是.NET正在执行其工作的结果。 - Dan Atkinson
所以我们应该展示一次迭代不是50毫秒,而是在常规情况下为0.03毫秒,这就是我的观点。 - Barreto
1
@BarretoFreekhealer 不,因为那将是完全误导和不正确的。在我的机器上,单次迭代37毫秒。运行多个迭代超过一个显然不再是一个迭代 - 这就是为什么我展示了1,000次迭代后的性能提升。 - Dan Atkinson
是的,但这只是一个迭代,如果您多次运行一个迭代,则不再与第一次相同,因此需要展示一个迭代并不总是32毫秒。 - Barreto
1
@BarretoFreekhealer 如果您多次运行单个迭代,那么它就不再是单个迭代了 - 这是定义! 您可以随意发布自己的答案来反驳并提供替代代码建议,但我不会更改或编辑我的答案。这是我对此事的最后评论。 - Dan Atkinson

3

3

Json有时比XML对人类不太易读,但是生成的文件大小较小。因此,如果您需要通过网络发送文件,则Json可能是更好的选择;或者如果您想要能够阅读它,则XML更好。另一个好处是,在.NET 4中,您拥有动态关键字,并且可以直接将Json转换为C#对象。


@Arijoon 哇...5年前的评论 :) 无论如何,最近我在开发Web应用程序,现在我发现它更易读了,因为我已经习惯了它,而且编辑器现在更加智能(你可以折叠JSON部分等)。除了一种情况:当您有一个包含换行符文本的值时。您无法真正格式化JSON文本值以包含新行,您必须添加\n。所以,您会看到一个包含\n的大行。 - Andrei Neagu

1

序列化的成本大致相同。不太可能有明显的差异。使用您的用户最舒适修改的格式(因为它是配置文件)。

真正的性能差异可能发生在您需要通过网络发送JSON或XML时。然后,性能取决于您要发送多少内容,由于JSON通常比XML更简洁,因此通常在网络上表现更好。


4
除非启用了压缩或使用二进制XML,否则无法实现。对于我的口味,OP的问题中有太多未明示的假设。 - Adam Houldsworth

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