如何提高JSON反序列化的速度在.net中? (JSON.net还是其他?) [英] How to improve JSON deserialization speed in .Net? (JSON.net or other?)

查看:224
本文介绍了如何提高JSON反序列化的速度在.net中? (JSON.net还是其他?)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在考虑更换(有的或多个)通过JSON(WCF或其他)称,经典SOAP XML WCF称由于较低的开销和易用性直接在Javascript。现在,我们刚刚增加了额外的Json端点,以我们的Web服务,并添加WebInvoke属性一些操作和测试它们。一切工作正常,使用C#.NET客户端或JavaScript客户端。到目前为止好。

不过,这似乎是反序列化大JSON字符串在C#.NET对象比反序列化SOAP XML慢得多。两者都是使用DataContract和DataMember属性(完全一样的DTO)。我的问题是:这正常吗?有什么我们可以做,以优化这样的表现?或者我们应该考虑JSON只对较小的请求,我们DO注意到性能改进。

现在,我们已经选择JSON.net本次测试,即使它并不在本次测试情况表明,它应该比净JSON序列化更快。不知怎的,ServiceStack反序列化根本不工作(没有错误,为的IList返回null)。

有关测试,我们做一个服务电话,收​​集室的列表。它返回一个GetRoomListResponse和的情况下返回5假人的房间,将JSON看起来是这样的:

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

的响应和DTO的是这样的:

  [DataContract(命名空间=喇嘛)
公共类GetRoomListResponse
{
    [数据成员]
    公众的IList&LT;房间&GT; RoomList;

    [数据成员]
    公共字符串例外;

    [数据成员]
    公共AcknowledgeType确认= AcknowledgeType.Success;

    [数据成员]
    公共字符串信息;

    [数据成员]
    公众诠释code;

    [数据成员]
    公众的IList&LT;字符串&GT; ValidateErrors;
}

[DataContract(名称为位置,命名空间=喇嘛)
公共类地点
{
    [数据成员]
    公众的Guid标识{获取;组; }

    [数据成员]
    公众诠释编号{获得;组; }

    [数据成员]
    公共字符串code {获得;组; }

    [数据成员]
    公共字符串描述{获得;组; }
}

[DataContract(名称为房,命名空间=喇嘛)
公共类房
{
    [数据成员]
    公众的Guid标识{获取;组; }

    [数据成员]
    公共字符串描述{获得;组; }

    [数据成员]
    公共位置位置{获得;组; }
}
 

那么,我们的测试code是如下:

 静态无效的主要(字串[] args)
    {
        SoapLogin();

        Console.WriteLine();

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

        Console.WriteLine();

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

        到Console.ReadLine();
    }

    私有静态无效SoapGetRoomList()
    {
        VAR请求=新TestServiceReference.GetRoomListRequest()
        {
            令牌=令牌,
        };

        秒表SW = Stopwatch.StartNew();

        使用(VAR的客户=新TestServiceReference.WARPServiceClient())
        {
            TestServiceReference.GetRoomListResponse响应= client.GetRoomList(要求);
        }

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

    私有静态无效JsonDotNetGetRoomList()
    {
        VAR请求=新GetRoomListRequest()
        {
            令牌=令牌,
        };

        秒表SW = Stopwatch.StartNew();
        长deserializationMillis;

        使用(Web客户端的客户端=新的Web客户端())
        {
            client.Headers [内容型] =应用/ JSON;
            client.Encoding = Encoding.UTF8;

            字符串=的RequestData JsonConvert.SerializeObject(请求,JsonSerializerSettings);

            VAR responseData = client.UploadString(GetRoomListAddress,的RequestData);

            秒表SW2 = Stopwatch.StartNew();
            VAR响应= JsonConvert.DeserializeObject&LT; GetRoomListResponse&GT;(responseData,JsonSerializerSettings);
            sw2.Stop();
            deserializationMillis = sw2.ElapsedMilliseconds;
        }

        sw.Stop();
        Console.WriteLine(JSON.Net GetRoomList:+ sw.ElapsedMilliseconds +(反序列化时间:+ deserializationMillis +));
    }

    私有静态JsonSerializerSettings JsonSerializerSettings
    {
        得到
        {
            VAR serializerSettings =新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;

            返回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(反序列化时间:91)
JSON.Net GetRoomList:3(反序列化时间:0)
JSON.Net GetRoomList:2(反序列化时间:0)
JSON.Net GetRoomList:2(反序列化时间:0)
JSON.Net GetRoomList:2(反序列化时间:0)
JSON.Net GetRoomList:2(反序列化时间:0)
JSON.Net GetRoomList:2(反序列化时间: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(反序列化时间:100)
JSON.Net GetRoomList:12(反序列化时间:8)
JSON.Net GetRoomList:12(反序列化时间:8)
JSON.Net GetRoomList:12(反序列化时间:8)
JSON.Net GetRoomList:11(反序列化时间:8)
JSON.Net GetRoomList:11(反序列化时间:8)
JSON.Net GetRoomList:15(反序列化时间: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(反序列化时间:175)
JSON.Net GetRoomList:107(反序列化时间:79)
JSON.Net GetRoomList:108(反序列化时间:82)
JSON.Net GetRoomList:112(反序列化时间:85)
JSON.Net GetRoomList:105(反序列化时间:79)
JSON.Net GetRoomList:111(反序列化时间:81)
JSON.Net GetRoomList:110(反序列化时间:82)
 

我跑在释放模式的应用程序。在同一台机器上客户端和服务器。正如你所看到的,许多反序列化对象(同类型)需要更多的时间使用JSON比XML映射到对象的WCF SOAP使用。丫的,反序列化本身就需要比整个使用SOAP Web服务调用更多的时间。

对此有一个解释?请问XML(或WCF SOAP实现)提供了一个很大的优势,在这方面还是有什么东西我可以在客户端改变(我宁可不改变服务,而且改变了客户端DTO的是可以接受的)尝试提高性能?这感觉就像我已经选择在JSON.net侧的一些设置,应该让快于默认设置,不是吗?是这里的瓶颈是什么?

解决方案

我花一点点的时间阅读有关JSON.NET内部,我的结论是,缓慢通过的反射而致

在JSON.NET网站,我已经找到了一些不错的性能提示,我试图pretty的每样东西(JObject.Parse,自定义转换器等),但我不能挤出任何显著的性能提升。然后我读过的最重要的注意事项对整个网站:

  

如果性能是重要的,你不介意多code得到它,那么这是你最佳的选择。了解更多关于使用JsonReader / JsonWriter这里

所以,我听取了意见和我实现了一个JsonReader的基本版本读取字符串有效:

  VAR读卡器=新JsonTextReader(新StringReader(jsonString));

VAR响应=新GetRoomListResponse();
VAR currentProperty =的String.Empty;

而(reader.Read())
{
    如果(reader.Value!= NULL)
    {
        如果(reader.TokenType == JsonToken.PropertyName)
            currentProperty = reader.Value.ToString();

        如果(reader.TokenType == JsonToken.Integer和放大器;&安培; currentProperty ==确认)
            response.Acknowledge =(AcknowledgeType)Int32.Parse(reader.Value.ToString());

        如果(reader.TokenType == JsonToken.Integer和放大器;&安培; currentProperty ==code)
            。响应code = Int32.Parse(reader.Value.ToString());

        如果(reader.TokenType == JsonToken.String和放大器;&安培; currentProperty ==消息)
            response.Message = reader.Value.ToString();

        如果(reader.TokenType == JsonToken.String和放大器;&安培; currentProperty ==异常)
            response.Exception = reader.Value.ToString();

        //处理室和其他的东西
    }
    其他
    {
        //过程跟踪当前嵌套元素
    }
}
 

我觉得运动是明确的,和毫无疑问,这是你可以走出JSON.NET的最佳性能

就在这有限的code比反序列化版本我有500间客房中12倍的速度更快,但当然映射未完成。但是,我是pretty的肯定,这将是比反序列化在最坏的情况下,至少5倍的速度更快。

看看这个链接有关JsonReader等​​详细信息如何使用它:

<一个href="http://james.newtonking.com/json/help/html/ReadingWritingJSON.htm">http://james.newtonking.com/json/help/html/ReadingWritingJSON.htm

We're considering replacing (some or many) 'classic' SOAP XML WCF calls by JSON (WCF or other) calls, because of the lower overhead and ease of use directly in Javascript. For now, we've just added an additional Json endpoint to our web service and added WebInvoke attributes to some operations and tested them. Everything works fine, using C# .Net clients or Javascript clients. So far so good.

However, it seems like deserializing big JSON strings to objects in C# .Net is much slower than deserializing SOAP XML. Both are using DataContract and DataMember attributes (exact same DTO). My question is: is this expected? Is there anything we can do to optimize this performance? Or should we consider JSON only for smaller requests where we DO notice performance improvements.

For now we've chosen JSON.net for this test and even though it doesn't show in this test case, it's supposed to be faster than the .Net JSON serialization. Somehow the ServiceStack deserialization does not work at all (no error, returns null for the IList).

For the test we do a service call to collect a list of rooms. It returns a GetRoomListResponse and in case of returning 5 dummy rooms, the JSON looks like this:

{"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}}]}

The Response and DTO's look like this:

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

Then our test code is as follows:

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

Now we've run this application with returning 50, 500 and 5000 rooms. The objects are not very complex.

These are the results; times are in ms:

50 rooms:

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 rooms:

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 rooms:

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)

I'm running the application in release mode. Both client and server on same machine. As you can see, deserialization of many (of the same type of) objects takes much more time with JSON than the XML to object mapping that WCF SOAP uses. Hell, deserialization alone takes more time than the entire web service call using SOAP.

Is there an explanation for this? Does XML (or the WCF SOAP implementation) offer a big advantage in this area or are there any things I can change on the client side (I'd rather not change the service, but changing the client side DTO's is acceptable) to try to improve performance? It feels like I already selected some settings on the JSON.net side that should make it faster than default settings, no? What seems to be the bottleneck here?

解决方案

I have spent a little bit more time reading about JSON.NET internals, and my conclusion is that the slowness is caused mostly by reflection.

On the JSON.NET site i have found some nice performance tips, and i tried pretty much everything (JObject.Parse, Custom Converters etc.) but i couldn't squeeze out any significant performance improvement. Then i read the most important note on the whole site:

If performance is important and you don't mind more code to get it then this is your best choice. Read more about using JsonReader/JsonWriter here

So i listened to the advice and i implemented a basic version of a JsonReader to read the string efficiently:

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

I think the exercise is clear, and without doubt this is the best performance you can get out of JSON.NET.

Just this limited code is 12x faster than the Deserialize version on my box with 500 rooms, but of course the mapping is not completed. However, i am pretty sure it will be at least 5x faster than deserialization in the worst-case.

Check out this link for more info about the JsonReader and how to use it:

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

这篇关于如何提高JSON反序列化的速度在.net中? (JSON.net还是其他?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆