JObject.Parse与JsonConvert.DeserializeObject的区别

110

JsonConvert.DeserializeObject和JObject.Parse有什么区别?就我所知,它们都接受一个字符串并在Json.NET库中。什么情况下使用其中一个更方便,还是只是个人喜好?

以下是我使用两者完成完全相同的操作的示例 - 解析Json字符串并返回其中一个Json属性的列表。

public ActionResult ReadJson()
{
    string countiesJson = "{'Everything':[{'county_name':null,'description':null,'feat_class':'Civil','feature_id':'36865',"
                    +"'fips_class':'H1','fips_county_cd':'1','full_county_name':null,'link_title':null,'url':'http://www.alachuacounty.us/','name':'Alachua County'"+ ",'primary_latitude':'29.7','primary_longitude':'-82.33','state_abbreviation':'FL','state_name':'Florida'},"+
                    "{'county_name':null,'description':null,"+ "'feat_class':'Civil','feature_id':'36866','fips_class':'H1','fips_county_cd':'3','full_county_name':null,'link_title':null,'url':'http://www.bakercountyfl.org/','name':'Baker County','primary_latitude':'30.33','primary_longitude':'-82.29','state_abbreviation':'FL','state_name':'Florida'}]}";

    //Can use either JSONParseObject or JSONParseDynamic here
    List<string> counties = JSONParseObject(countiesJson);
    JSONParseDynamic(countiesJson);
    return View(counties);
}

public List<string> JSONParseObject(string jsonText)
{
    JObject jResults = JObject.Parse(jsonText);
    List<string> counties = new List<string>();
    foreach (var county in jResults["Everything"])
    {
        counties.Add((string)county["name"]);
    }
    return counties;
}

public List<string> JSONParseDynamic(string jsonText)
{
    dynamic jResults = JsonConvert.DeserializeObject(jsonText);
    List<string> counties = new List<string>();
    foreach(var county in jResults.Everything)
    {
        counties.Add((string)county.name);
    }
    return counties;
}
5个回答

133
LINQ-to-JSON API(例如JObject,JToken等)的存在是为了使处理JSON时无需事先知道其结构。您可以使用JToken.Parse反序列化任何任意的JSON,然后使用其他JToken方法来检查和操作其内容。如果您只需要JSON中的一个或两个值(例如县的名称),LINQ-to-JSON也可以很好地工作。
另一方面,JsonConvert.DeserializeObject主要用于在预先知道JSON结构并且想要反序列化为强类型类时使用。例如,以下是如何将JSON中的完整县数据获取到一个County对象列表中的示例。
class Program
{
    static void Main(string[] args)
    {
        string countiesJson = "{'Everything':[{'county_name':null,'description':null,'feat_class':'Civil','feature_id':'36865',"
                +"'fips_class':'H1','fips_county_cd':'1','full_county_name':null,'link_title':null,'url':'http://www.alachuacounty.us/','name':'Alachua County'"+ ",'primary_latitude':'29.7','primary_longitude':'-82.33','state_abbreviation':'FL','state_name':'Florida'},"+
                "{'county_name':null,'description':null,"+ "'feat_class':'Civil','feature_id':'36866','fips_class':'H1','fips_county_cd':'3','full_county_name':null,'link_title':null,'url':'http://www.bakercountyfl.org/','name':'Baker County','primary_latitude':'30.33','primary_longitude':'-82.29','state_abbreviation':'FL','state_name':'Florida'}]}";

        foreach (County c in JsonParseCounties(countiesJson))
        {
            Console.WriteLine(string.Format("{0}, {1} ({2},{3})", c.name, 
               c.state_abbreviation, c.primary_latitude, c.primary_longitude));
        }
    }

    public static List<County> JsonParseCounties(string jsonText)
    {
        return JsonConvert.DeserializeObject<RootObject>(jsonText).Counties;
    }
}

public class RootObject
{
    [JsonProperty("Everything")]
    public List<County> Counties { get; set; }
}

public class County
{
    public string county_name { get; set; }
    public string description { get; set; }
    public string feat_class { get; set; }
    public string feature_id { get; set; }
    public string fips_class { get; set; }
    public string fips_county_cd { get; set; }
    public string full_county_name { get; set; }
    public string link_title { get; set; }
    public string url { get; set; }
    public string name { get; set; }
    public string primary_latitude { get; set; }
    public string primary_longitude { get; set; }
    public string state_abbreviation { get; set; }
    public string state_name { get; set; }
}

请注意,Json.Net使用传递给JsonConvert.DeserializeObject方法的类型参数来确定要创建的对象类型。

当然,如果您在调用DeserializeObject时不指定类型,或者使用objectdynamic,那么Json.Net将不得不反序列化为一个JObject。(您可以通过检查jResults.GetType().FullName来查看动态变量实际上包含了一个JObject。)因此,在这种情况下,JsonConvert.DeserializeObjectJToken.Parse之间没有太大的区别;两者都会给出相同的结果。


3
我希望这些内容可以在官方文档中找到。 - Alex Angas
1
DeserializeObject在处理JSON时是否容忍存在于类中不存在的额外属性?它会忽略这些属性还是抛出异常? - Michael Freidgeim
1
@MichaelFreidgeim 这由 MissingMemberHandling 设置控制。默认情况下是忽略额外的属性。 - Brian Rogers
从性能上来看,将对象反序列化为动态对象与将其反序列化为强类型类相比,平均而言哪个更快?我可以看到一个比另一个更快的不同原因,但我想知道是否使用动态对象会更快,因为它不必使用反射? - Dinerdo
这两者在抛出异常方面有区别吗?我遇到了一些代码,首先调用JObject.Parse(response),然后是 JsonConvert.DeserializeObject<MyType>(response)。我试图确定这是否是一些设计来在某些条件下抛出异常以避免调用DeserializeObject的黑客,还是它设置了一些由JsonConvert.DeserializeObject使用的状态...或者它只是无意义的代码。 - xr280xr
显示剩余2条评论

39

JsonConvert.DeserializeObject相较于JObject.Parse具有一个优势: 可以使用自定义的JsonSerializerSettings。

这在需要控制如何反序列化日期时非常有用。 默认情况下,日期被反序列化为DateTime对象。 这意味着你可能会得到一个带有不同时区的日期,而不是json字符串中的时区。

你可以通过创建JsonSerializerSetting并将DateParseHandling设置为DateParseHandling.DateTimeOffset来更改此行为。

一个例子:

var json = @"{ ""Time"": ""2015-10-28T14:05:22.0091621+00:00""}";
Console.WriteLine(json);
// Result: { "Time": "2015-10-28T14:05:22.0091621+00:00" }

var jObject1 = JObject.Parse(json);
Console.WriteLine(jObject1.ToString());
// Result: { "Time": "2015-10-28T15:05:22.0091621+01:00" }

var jObject2 = Newtonsoft.Json.JsonConvert.DeserializeObject(json, 
  new Newtonsoft.Json.JsonSerializerSettings 
  { 
    DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset 
  });
Console.WriteLine(jObject2.ToString());
// Result: { "Time": "2015-10-28T14:05:22.0091621+00:00" }

如果您确切知道要使用哪个类(即非动态),使用DeserializeObject是否也会更快? - Dinerdo

2

对我来说,我感兴趣的关键区别是速度。

我进行了一个简单的测试,以找出使用JToken.Parse(string)还是DeserializeObject<JToken>(string)来创建大量JToken更快的方法,并在使用真实世界数据样本进行2,000,000次迭代后获得以下结果:

方法 操作系统滴答数 毫秒数
JsonConvert 86123945 8612ms
JToken 67671724 6767ms

各次运行结果有些差异,但差异始终很大。

这里是测试代码,你可以修改它或自行运行:

    private static string s = @"{'stream':'btcusdt @bookTicker','data':{'u':20430107433,'s':'BTCUSDT','b':'21223.72000000','B':'3.29440000','a':'21223.73000000','A':'2.05450000'}}";

    private static Stopwatch sw = new Stopwatch();

    private static void Main(string[] args)
    {
        JToken convert = default;
        sw.Restart();
        for (int i = 0; i < 2000000; i++)
        {
            convert = JsonConvert.DeserializeObject<JToken>(s);
        }

        Console.WriteLine("JsonConvert: " + sw.ElapsedTicks + " [" + sw.ElapsedMilliseconds + "ms]");
        convert.ToString();

        convert = default;
        sw.Restart();
        for (int i = 0; i < 2000000; i++)
        {
            convert = JToken.Parse(s);
        }

        Console.WriteLine("JToken     : " + sw.ElapsedTicks + " [" + sw.ElapsedMilliseconds + "ms]");
        convert.ToString();

        Console.ReadLine();
    }

0
除了这里提供的关于使用的答案,我认为是正确的: Jobject.Parse -> 当Json不是强类型或者您事先不知道Json的结构时使用

JsonConvert.DeserializeObject<T> -> 当您知道要将Json转换成哪个类或类型时使用。 T 可以是一个复杂的类或一个简单的类型

我的答案基于在不知道结构的情况下性能表现,如在OP代码中给出的那样,如果我们对两种方法的性能进行基准测试,可以发现Jobject.Parse()在分配内存方面表现良好,请忽略方法名称,我首先调用带有'JsonConvert.DeserializeObject'的方法,然后第二个方法是使用Jobject.Parse

enter image description here


你在两种情况下都将反序列化为JObject吗? - Jon Miller

0

我知道JsonConvert.DeserializeObject的一个优点是可以直接反序列化数组/列表的json文本,但JObject不行。

尝试以下示例代码:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace NetCoreJsonNETDemo
{
    internal class Person
    {
        [JsonProperty]
        internal string Name
        {
            get;
            set;
        }

        [JsonProperty]
        internal int? Age
        {
            get;
            set;
        }
    }

    internal class PersonContainer
    {
        public List<Person> Persons
        {
            get;
            set;
        }
    }

    class Program
    {
        static T RecoverPersonsWithJsonConvert<T>(string json)
        {
            return JsonConvert.DeserializeObject<T>(json);
        }

        static T RecoverPersonsWithJObejct<T>(string json) where T : class
        {
            try
            {
                return JObject.Parse(json).ToObject<T>();
            }
            catch (Exception ex)
            {
                Console.WriteLine("JObject threw an Exception: " + ex.Message);
                return null;
            }
        }

        static void Main(string[] args)
        {
            List<Person> persons = new List<Person>();

            persons.Add(new Person()
            {
                Name = "Jack",
                Age = 18
            });

            persons.Add(new Person()
            {
                Name = "Sam",
                Age = null
            });

            persons.Add(new Person()
            {
                Name = "Bob",
                Age = 36
            });

            string json = JsonConvert.SerializeObject(persons, new JsonSerializerSettings()
            {
                Formatting = Formatting.Indented
            });

            List<Person> newPersons = RecoverPersonsWithJsonConvert<List<Person>>(json);
            newPersons = RecoverPersonsWithJObejct<List<Person>>(json);//JObject will throw an error, since the json text is an array.

            PersonContainer personContainer = new PersonContainer()
            {
                Persons = persons
            };

            json = JsonConvert.SerializeObject(personContainer, new JsonSerializerSettings()
            {
                Formatting = Formatting.Indented
            });

            newPersons = RecoverPersonsWithJObejct<PersonContainer>(json).Persons;

            newPersons = null;
            newPersons = RecoverPersonsWithJsonConvert<PersonContainer>(json).Persons;

            Console.WriteLine("Press any key to end...");
            Console.ReadKey();
        }
    }
}

2
JToken.Parse() 可以 :) - Frode Nilsen
1
@FrodeNilsen 另外,根据我的测试,JToken.Parse() 似乎是此页面上所有动态/反序列化方法中最快的。 - Mason G. Zhwiti

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