当前位置: 首页 > 面试题库 >

如何提高.Net中的JSON反序列化速度?(JSON.net还是其他?)

曹泉
2023-03-14
问题内容

我们正在考虑用JSON(WCF或其他)调用替换(一些或许多)“经典” SOAP XML
WCF调用,因为其开销较低且易于直接在Javascript中使用。现在,我们刚刚在Web服务中添加了一个额外的Json端点,并在某些操作中添加了WebInvoke属性并对其进行了测试。使用C#.Net客户端或Javascript客户端,一切正常。到目前为止,一切都很好。

但是,似乎将大JSON字符串反序列化为C#.Net中的对象比反序列化SOAP
XML慢得多。两者都使用DataContract和DataMember属性(完全相同的DTO)。我的问题是:这是预期的吗?我们可以做些什么来优化此性能?或者,我们应该只对较小的请求考虑JSON,但确实注意到性能有所提高。

到目前为止,我们已经选择JSON.net进行此测试,即使在此测试用例中未显示它,也应该比.Net
JSON序列化要快。不知何故,ServiceStack反序列化根本不起作用(无错误,为IList返回null)。

为了进行测试,我们进行了服务呼叫以收集房间列表。它返回一个GetRoomListResponse,并且在返回5个虚拟房间的情况下,JSON如下所示:

{"Acknowledge":1,"Code":0,"Message":null,"ValidateErrors":null,"Exception":null,"RoomList":[{"Description":"DummyRoom","Id":"205305e6-9f7b-4a6a-a1de-c5933a45cac0","Location":{"Code":"123","Description":"Location 123","Id":"4268dd65-100d-47c8-a7fe-ea8bf26a7282","Number":5}},{"Description":"DummyRoom","Id":"aad737f7-0caa-4574-9ca5-f39964d50f41","Location":{"Code":"123","Description":"Location 123","Id":"b0325ff4-c169-4b56-bc89-166d4c6d9eeb","Number":5}},{"Description":"DummyRoom","Id":"c8caef4b-e708-48b3-948f-7a5cdb6979ef","Location":{"Code":"123","Description":"Location 123","Id":"11b3f513-d17a-4a00-aebb-4d92ce3f9ae8","Number":5}},{"Description":"DummyRoom","Id":"71376c49-ec41-4b12-b5b9-afff7da882c8","Location":{"Code":"123","Description":"Location 123","Id":"1a188f13-3be6-4bde-96a0-ef5e0ae4e437","Number":5}},{"Description":"DummyRoom","Id":"b947a594-209e-4195-a2c8-86f20eb883c4","Location":{"Code":"123","Description":"Location 123","Id":"053e9969-d0ed-4623-8a84-d32499b5a8a8","Number":5}}]}

响应和DTO如下所示:

[DataContract(Namespace = "bla")]
public class GetRoomListResponse
{
    [DataMember]
    public IList<Room> RoomList;

    [DataMember]
    public string Exception;

    [DataMember]
    public AcknowledgeType Acknowledge = AcknowledgeType.Success;

    [DataMember]
    public string Message;

    [DataMember]
    public int Code;

    [DataMember]
    public IList<string> ValidateErrors;
}

[DataContract(Name = "Location", Namespace = "bla")]
public class Location
{
    [DataMember]
    public Guid Id { get; set; }

    [DataMember]
    public int Number { get; set; }

    [DataMember]
    public string Code { get; set; }

    [DataMember]
    public string Description { get; set; }
}

[DataContract(Name = "Room", Namespace = "bla")]
public class Room
{
    [DataMember]
    public Guid Id { get; set; }

    [DataMember]
    public string Description { get; set; }

    [DataMember]
    public Location Location { get; set; }
}

然后我们的测试代码如下:

    static void Main(string[] args)
    {
        SoapLogin();

        Console.WriteLine();

        SoapGetRoomList();
        SoapGetRoomList();
        SoapGetRoomList();
        SoapGetRoomList();
        SoapGetRoomList();
        SoapGetRoomList();
        SoapGetRoomList();

        Console.WriteLine();

        JsonDotNetGetRoomList();
        JsonDotNetGetRoomList();
        JsonDotNetGetRoomList();
        JsonDotNetGetRoomList();
        JsonDotNetGetRoomList();
        JsonDotNetGetRoomList();
        JsonDotNetGetRoomList();

        Console.ReadLine();
    }

    private static void SoapGetRoomList()
    {
        var request = new TestServiceReference.GetRoomListRequest()
        {
            Token = Token,
        };

        Stopwatch sw = Stopwatch.StartNew();

        using (var client = new TestServiceReference.WARPServiceClient())
        {
            TestServiceReference.GetRoomListResponse response = client.GetRoomList(request);
        }

        sw.Stop();
        Console.WriteLine("SOAP GetRoomList: " + sw.ElapsedMilliseconds);
    }

    private static void JsonDotNetGetRoomList()
    {
        var request = new GetRoomListRequest()
        {
            Token = Token,
        };

        Stopwatch sw = Stopwatch.StartNew();
        long deserializationMillis;

        using (WebClient client = new WebClient())
        {
            client.Headers["Content-type"] = "application/json";
            client.Encoding = Encoding.UTF8;

            string requestData = JsonConvert.SerializeObject(request, JsonSerializerSettings);

            var responseData = client.UploadString(GetRoomListAddress, requestData);

            Stopwatch sw2 = Stopwatch.StartNew();
            var response = JsonConvert.DeserializeObject<GetRoomListResponse>(responseData, JsonSerializerSettings);
            sw2.Stop();
            deserializationMillis = sw2.ElapsedMilliseconds;
        }

        sw.Stop();
        Console.WriteLine("JSON.Net GetRoomList: " + sw.ElapsedMilliseconds + " (deserialization time: " + deserializationMillis + ")");
    }

    private static JsonSerializerSettings JsonSerializerSettings
    {
        get
        {
            var serializerSettings = new JsonSerializerSettings();

            serializerSettings.CheckAdditionalContent = false;
            serializerSettings.ConstructorHandling = ConstructorHandling.Default;
            serializerSettings.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
            serializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
            serializerSettings.NullValueHandling = NullValueHandling.Ignore;
            serializerSettings.ObjectCreationHandling = ObjectCreationHandling.Replace;
            serializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.None;
            serializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Error;

            return serializerSettings;
        }
    }

现在,我们以返回50、500和5000个房间的方式运行了该应用程序。对象不是很复杂。

这些是结果;时间以毫秒为单位:

50间客房:

SOAP GetRoomList: 37
SOAP GetRoomList: 5
SOAP GetRoomList: 4
SOAP GetRoomList: 4
SOAP GetRoomList: 9
SOAP GetRoomList: 5
SOAP GetRoomList: 5

JSON.Net GetRoomList: 289 (deserialization time: 91)
JSON.Net GetRoomList: 3 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)

500间客房:

SOAP GetRoomList: 47
SOAP GetRoomList: 9
SOAP GetRoomList: 8
SOAP GetRoomList: 8
SOAP GetRoomList: 8
SOAP GetRoomList: 8
SOAP GetRoomList: 8

JSON.Net GetRoomList: 301 (deserialization time: 100)
JSON.Net GetRoomList: 12 (deserialization time: 8)
JSON.Net GetRoomList: 12 (deserialization time: 8)
JSON.Net GetRoomList: 12 (deserialization time: 8)
JSON.Net GetRoomList: 11 (deserialization time: 8)
JSON.Net GetRoomList: 11 (deserialization time: 8)
JSON.Net GetRoomList: 15 (deserialization time: 12)

5000间客房:

SOAP GetRoomList: 93
SOAP GetRoomList: 51
SOAP GetRoomList: 58
SOAP GetRoomList: 60
SOAP GetRoomList: 53
SOAP GetRoomList: 53
SOAP GetRoomList: 51

JSON.Net GetRoomList: 405 (deserialization time: 175)
JSON.Net GetRoomList: 107 (deserialization time: 79)
JSON.Net GetRoomList: 108 (deserialization time: 82)
JSON.Net GetRoomList: 112 (deserialization time: 85)
JSON.Net GetRoomList: 105 (deserialization time: 79)
JSON.Net GetRoomList: 111 (deserialization time: 81)
JSON.Net GetRoomList: 110 (deserialization time: 82)

我正在发布模式下运行该应用程序。客户端和服务器都在同一台计算机上。如您所见,与WCF
SOAP使用的XML到对象的映射相比,使用JSON对许多(相同类型的)对象进行反序列化要花费更多的时间。糟糕的是,反序列化比使用SOAP进行整个Web服务调用要花费更多的时间。

对此有解释吗?XML(或WCF
SOAP实现)在这方面是否提供了很大的优势,或者我可以在客户端进行任何更改(我宁愿不更改服务,但可以更改客户端DTO可以接受)以尝试提高绩效?感觉好像我已经在JSON.net端选择了一些设置,这些设置应该使其比默认设置更快,不是吗?这里的瓶颈似乎是什么?


问题答案:

我花了更多时间阅读有关JSON.NET内部的信息,我的结论是,速度缓慢主要是由 反射 引起的。

在JSON.NET站点上,我发现了一些不错的性能提示,并且尝试了几乎所有方法(JObject.Parse,自定义转换器等),但是我无法实现任何显着的性能改进。然后,我阅读了整个网站上最重要的说明:

如果性能很重要,并且您不介意获得更多代码,那么这是您的最佳选择。在此处阅读有关使用JsonReader / JsonWriter的更多信息

因此,我听了建议,并实现了JsonReader的基本版本以有效地读取字符串:

var reader = new JsonTextReader(new StringReader(jsonString));

var response = new GetRoomListResponse();
var currentProperty = string.Empty;

while (reader.Read())
{
    if (reader.Value != null)
    {
        if (reader.TokenType == JsonToken.PropertyName)
            currentProperty = reader.Value.ToString();

        if (reader.TokenType == JsonToken.Integer && currentProperty == "Acknowledge")
            response.Acknowledge = (AcknowledgeType)Int32.Parse(reader.Value.ToString());

        if (reader.TokenType == JsonToken.Integer && currentProperty == "Code")
            response.Code = Int32.Parse(reader.Value.ToString());

        if (reader.TokenType == JsonToken.String && currentProperty == "Message")
            response.Message = reader.Value.ToString();

        if (reader.TokenType == JsonToken.String && currentProperty == "Exception")
            response.Exception = reader.Value.ToString();

        // Process Rooms and other stuff
    }
    else
    {
        // Process tracking the current nested element
    }
}

我认为练习很清楚, 毫无疑问,这是您可以从JSON.NET中获得的最佳性能

仅此有限的代码比Deserialize我有500个房间的盒子上的版本快12倍,但是映射当然没有完成。但是,我很确定在最坏的情况下它至少比反序列化快5倍。

查看此链接,以获取有关JsonReader以及如何使用它的更多信息:

http://james.newtonking.com/json/help/html/ReadingWritingJSON.htm



 类似资料:
  • 问题内容: 我有一个非常不理想的情况,要求我反序列化JSON,其中值是JSON.NET的字段名称。假设我具有以下结构正确的JSON: 使用JSON.NET将其反序列化为CLR对象非常容易: 但是,在当前情况下,我有以下可怕的JSON,就值而言,它等效于上述JSON: 如您所见,field不是数组。这是一个包含其他值作为对象的对象,其唯一键作为其字段名称(这太可怕了)。使用JSON.NET 将JSO

  • 问题内容: 我想避免在序列化数据时重新发明轮子。我知道一些序列化彼此链接的对象的方法,但是范围从编写一些代码到编写大量用于序列化的代码,我希望避免这种情况。必须有一些通用的解决方案。 假设我有一个这样的结构: 我想将数据序列化为这样的东西: 在此,假设名称是唯一的,则链接仅作为名称序列化。链接也可以是“ family.persons.0”或生成的唯一ID等。 要求: 格式必须是 人类可读的 ,最好

  • 问题内容: 我正在使用以下类型定义Json.NET属性,然后在其方法内部使用Json.NET对其进行序列化: 我的问题是,它将返回“ {}”。这是为什么?以前在工作。我所做的唯一更改是更改了OptIn而不是OptOut,因为我希望包含的属性少于保留的属性。 问题答案: 如Newtonsoft在本期中所述,Json.NET实际上支持属性。但是,当相应的“真实”成员是属性时,似乎Json.NET要求成

  • 问题内容: 想知道您是否可以帮助我创建一个VB.Net类,在其中可以反序列化以下JSON响应: 我有以下几点: 和 我正在使用Newtonsoft.Json反序列化: 如果我知道actor元素始终遵循相同的格式- 有没有一种方法可以解析JSON响应,以便Player.Likes.Actors是一个List(Of Actor)而不是我现在拥有的List(OfList(Of String))? 问题答

  • 本文向大家介绍.NET中JSON的序列化和反序列化的几种方式,包括了.NET中JSON的序列化和反序列化的几种方式的使用技巧和注意事项,需要的朋友参考一下 一、什么是JSON JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立 于编程语言的文本格式来存

  • 问题内容: 我正在尝试使用自定义将一些JSON反序列化为各种子类 我几乎遵循了这一点。 我的抽象基类: 而我的派生: 只是一个包含一些字符串常量的静态类。 JSON文件反序列化如下: 现在,我的问题是,每当我运行代码时,都会出现以下错误: 有问题的Json文件如下所示: 在我看来,反序列化嵌套对象有麻烦,我该如何解决? 问题答案: 首先,错过了您的json中的菜单项“ Submenus Test”