JObject.Parse vs JsonConvert.DeserializeObject

85

Jaka jest różnica między JsonConvert.DeserializeObject i JObject.Parse? O ile wiem, oba pobierają ciąg i znajdują się w bibliotece Json.NET. Jaka sytuacja sprawiłaby, że jedna byłaby wygodniejsza od drugiej, czy jest to głównie preferencja?

Dla porównania, oto przykład, w którym używam obu, aby zrobić dokładnie to samo - przeanalizować ciąg Json i zwrócić listę jednego z atrybutów 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;
}
hubatish
źródło

Odpowiedzi:

91

API LINQ-JSON ( JObject, JTokenistnieje, itd.) W celu umożliwienia pracy z JSON bez konieczności poznać jego strukturę z wyprzedzeniem. Możesz deserializować dowolny dowolny kod JSON przy użyciu JToken.Parse, a następnie badać jego zawartość i manipulować nią przy użyciu innych JTokenmetod. LINQ-to-JSON działa również dobrze, jeśli potrzebujesz tylko jednej lub dwóch wartości z JSON (takich jak nazwa hrabstwa).

JsonConvert.DeserializeObject, z drugiej strony, jest przeznaczony głównie do użytku, gdy znasz strukturę JSON z wyprzedzeniem i chcesz deserializować do klas o silnym typie. Na przykład, oto jak zamieścić pełny zestaw danych hrabstwa z JSON na liście Countyobiektów.

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

Zwróć uwagę, że Json.Net używa argumentu type nadanego JsonConvert.DeserializeObjectmetodzie w celu określenia typu obiektu do utworzenia.

Oczywiście, jeśli nie określisz typu podczas wywołania DeserializeObjectlub użyjesz objectlub dynamic, wówczas Json.Net nie ma innego wyjścia, jak tylko deserializować do pliku JObject. (Możesz sam przekonać się, że zmienna dynamiczna faktycznie zawiera a JObject, sprawdzając jResults.GetType().FullName.) W takim przypadku nie ma dużej różnicy między JsonConvert.DeserializeObjecti JToken.Parse; albo da ten sam wynik.

Brian Rogers
źródło
Dzięki za przemyślaną odpowiedź! Deskryptory Object vs Dynamic mają teraz sens. Podany przykład jest również świetny - wygląda o wiele łatwiej niż w przypadku JsonParseDynamic.
hubatish
3
Chciałbym, żeby to było w oficjalnych dokumentach .
Alex Angas
1
Czy DeserializeObject jest odporny na dodatkowe właściwości w json, które nie istnieją w klasie. Czy zignoruje je lub zgłosi wyjątek?
Michael Freidgeim
1
@MichaelFreidgeim Jest kontrolowane przez MissingMemberHandlingustawienie. Domyślnie ignorowane są dodatkowe właściwości.
Brian Rogers
Jeśli chodzi o wydajność, czy deserializacja do obiektu dynamicznego będzie średnio wolniejsza lub szybsza niż deserializacja do klasy o jednoznacznie określonym typie? Widzę różne powody, dla których jeden jest szybszy od drugiego, ale zastanawiam się, czy użycie dynamiki może być szybsze, ponieważ nie musi używać odbicia?
Dinerdo
27

JsonConvert.DeserializeObject ma jedną przewagę nad JObject.Parse: możliwe jest użycie niestandardowego JsonSerializerSettings.

Może to być bardzo przydatne, np. Jeśli chcesz kontrolować sposób deserializacji dat. Domyślnie daty są deserializowane do obiektów DateTime. Oznacza to, że możesz skończyć z datą z inną strefą czasową niż ta w ciągu json.

Możesz zmienić to zachowanie, tworząc JsonSerializerSetting i ustawiając DateParseHandling na DateParseHandling.DateTimeOffset.

Przykład:

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" }
pberggreen
źródło
Czy nie powinno też być szybsze korzystanie z DeserializeObject, jeśli dokładnie wiesz, do której klasy zamierzasz (tj. Nie jest dynamiczna)?
Dinerdo
0

Znałem zaletę, że JsonConvert.DeserializeObject może bezpośrednio deserializować tekst json Array / List , ale JObject nie może.

Wypróbuj poniższy przykładowy kod:

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();
        }
    }
}
Scott.Hu
źródło
1
JToken.Parse () może :)
Frode Nilsen
1
@FrodeNilsen Również JToken.Parse () wydaje się być najszybszą ze wszystkich metod dynamicznych / deserializacji na tej stronie, zgodnie z moimi testami.
Mason G. Zhwiti
0

Oprócz odpowiedzi udzielonych tutaj na temat użytkowania, które są poprawne według mnie: Jobject.Parse-> gdy Json nie jest silnie wpisany lub nie znasz struktury Json z wyprzedzeniem

JsonConvert.DeserializeObject<T>-> Kiedy wiesz, w której klasie lub typie rzutować Json. TMoże to być klasa złożona lub typ prosty

Moja odpowiedź opiera się na wydajności w przypadku, gdy struktura nie jest znana, jak podano w kodzie OP, jeśli porównamy użycie obu metod dla wydajności, zauważam, że Jobject.Parse()wypada dobrze pod względem przydzielonej pamięci, zignoruj ​​nazwę metod, najpierw wywołuję metodę z `` JsonConvert.DeserializeObject '', a następnie drugą metodą jestJobject.Parse

wprowadź opis obrazu tutaj

Rajeev Sirohi
źródło