JObject.Parse vs JsonConvert.DeserializeObject

83

Was ist der Unterschied zwischen JsonConvert.DeserializeObject und JObject.Parse? Soweit ich das beurteilen kann, nehmen beide einen String und befinden sich in der Json.NET-Bibliothek. Welche Situation würde eine bequemer machen als die andere, oder ist es hauptsächlich nur eine Präferenz?

Als Referenz sehen Sie hier ein Beispiel, in dem ich beide verwende, um genau dasselbe zu tun: Analysieren Sie eine Json-Zeichenfolge und geben Sie eine Liste eines der Json-Attribute zurück.

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
quelle

Antworten:

89

Der LINQ-to-JSON API ( JObject, JTokenusw.) existiert mit JSON arbeiten zu lassen , ohne dass die Struktur im voraus wissen. Sie können jeden beliebigen JSON mit deserialisieren JToken.Parseund dann seinen Inhalt mit anderen JTokenMethoden untersuchen und bearbeiten . LINQ-to-JSON funktioniert auch gut, wenn Sie nur einen oder zwei Werte aus dem JSON benötigen (z. B. den Namen eines Landkreises).

JsonConvert.DeserializeObjectAuf der anderen Seite soll es hauptsächlich verwendet werden, wenn Sie die Struktur des JSON im Voraus kennen und in stark typisierte Klassen deserialisieren möchten. So erhalten Sie beispielsweise den vollständigen Satz von County-Daten aus Ihrem JSON in eine Liste von CountyObjekten.

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

Beachten Sie, dass Json.Net das der JsonConvert.DeserializeObjectMethode angegebene Typargument verwendet, um zu bestimmen, welcher Objekttyp erstellt werden soll.

Wenn Sie beim Aufrufen DeserializeObjectoder Verwenden von objectoder keinen Typ angeben dynamic, hat Json.Net natürlich keine andere Wahl, als in einen zu deserialisieren JObject. (Sie können selbst sehen, dass Ihre dynamische Variable tatsächlich eine enthält, JObjectindem Sie prüfen jResults.GetType().FullName.) In diesem Fall gibt es also keinen großen Unterschied zwischen JsonConvert.DeserializeObjectund JToken.Parse; Beides führt zu demselben Ergebnis.

Brian Rogers
quelle
Danke für die durchdachte Antwort! Die Deskriptoren Object vs Dynamic sind jetzt sinnvoll. Das Beispiel, das Sie geben, ist auch großartig - das sieht viel einfacher aus als mit JsonParseDynamic.
Hubatish
3
Ich wünschte, dies wäre in den offiziellen Dokumenten .
Alex Angas
1
Ist DeserializeObject tolerant gegenüber zusätzlichen Eigenschaften in json, die in der Klasse nicht vorhanden sind? Wird es sie ignorieren oder eine Ausnahme auslösen?
Michael Freidgeim
1
@ MichaelFreidgeim Es wird von der MissingMemberHandlingEinstellung gesteuert . Standardmäßig werden zusätzliche Eigenschaften ignoriert.
Brian Rogers
Wird die Deserialisierung auf ein dynamisches Objekt in Bezug auf die Leistung im Durchschnitt langsamer oder schneller sein als die Deserialisierung auf eine stark typisierte Klasse? Ich kann verschiedene Gründe dafür sehen, dass einer schneller ist als der andere, aber ich frage mich, ob die Verwendung von Dynamik schneller sein kann, weil keine Reflexion erforderlich ist.
Dinerdo
27

JsonConvert.DeserializeObject hat einen Vorteil gegenüber JObject.Parse: Es ist möglich, benutzerdefinierte JsonSerializerSettings zu verwenden.

Dies kann sehr nützlich sein, z. B. wenn Sie steuern möchten, wie Daten deserialisiert werden. Standardmäßig werden Datumsangaben in DateTime-Objekte deserialisiert. Dies bedeutet, dass Sie möglicherweise ein Datum mit einer anderen Zeitzone als der in der JSON-Zeichenfolge haben.

Sie können dieses Verhalten ändern, indem Sie eine JsonSerializerSetting erstellen und DateParseHandling auf DateParseHandling.DateTimeOffset setzen.

Ein Beispiel:

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
quelle
Sollte es nicht auch schneller sein, DeserializeObject zu verwenden, wenn Sie genau wissen, zu welcher Klasse Sie gehen (dh nicht dynamisch)?
Dinerdo
0

Ich wusste, dass JsonConvert.DeserializeObject einen Array / List- Json-Text direkt deserialisieren kann, JObject jedoch nicht.

Versuchen Sie es mit dem folgenden Beispielcode:

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
quelle
1
JToken.Parse () kann :)
Frode Nilsen
1
@FrodeNilsen Außerdem scheint JToken.Parse () laut meinen Tests die schnellste aller dynamischen / Deserialisierungsmethoden auf dieser Seite zu sein.
Mason G. Zhwiti
0

Abgesehen von den hier Jobject.Parsegegebenen Antworten zur Verwendung, die meiner Meinung nach korrekt sind: -> wenn der Json nicht stark typisiert ist oder Sie die Struktur von Json nicht im Voraus kennen

JsonConvert.DeserializeObject<T>-> Wenn Sie wissen, in welche Klasse oder welchen Typ der Json umgewandelt werden soll, Tkann dies eine komplexe Klasse oder ein einfacher Typ sein

Meine Antwort basiert auf der Leistung für den Fall, dass die Struktur nicht bekannt ist, wie im OP-Code angegeben. Wenn wir die Verwendung beider Methoden für die Leistung vergleichen, wird festgestellt, dass die Leistung Jobject.Parse()in Bezug auf den zugewiesenen Speicher gut ist. Bitte ignorieren Sie den Namen Von den Methoden rufe ich zuerst die Methode mit 'JsonConvert.DeserializeObject' auf und dann die zweite Methode mitJobject.Parse

Geben Sie hier die Bildbeschreibung ein

Rajeev Sirohi
quelle