Désérialisation des données JSON en C # à l'aide de JSON.NET

144

Je suis relativement nouveau dans le travail avec les données C # et JSON et je cherche des conseils. J'utilise C # 3.0, avec .NET3.5SP1 et JSON.NET 3.5r6.

J'ai une classe C # définie que je dois remplir à partir d'une structure JSON. Cependant, toutes les structures JSON d'une entrée extraite du service Web ne contiennent pas tous les attributs possibles définis dans la classe C #.

J'ai fait ce qui semble être de la mauvaise manière et difficile et j'ai simplement choisi chaque valeur une par une dans le JObject et transformé la chaîne en la propriété de classe souhaitée.

JsonSerializer serializer = new JsonSerializer();
var o = (JObject)serializer.Deserialize(myjsondata);

MyAccount.EmployeeID = (string)o["employeeid"][0];

Quelle est la meilleure façon de désérialiser une structure JSON dans la classe C # et de gérer les éventuelles données manquantes de la source JSON?

Ma classe est définie comme:

  public class MyAccount
  {

    [JsonProperty(PropertyName = "username")]
    public string UserID { get; set; }

    [JsonProperty(PropertyName = "givenname")]
    public string GivenName { get; set; }

    [JsonProperty(PropertyName = "sn")]
    public string Surname { get; set; }

    [JsonProperty(PropertyName = "passwordexpired")]
    public DateTime PasswordExpire { get; set; }

    [JsonProperty(PropertyName = "primaryaffiliation")]
    public string PrimaryAffiliation { get; set; }

    [JsonProperty(PropertyName = "affiliation")]
    public string[] Affiliation { get; set; }

    [JsonProperty(PropertyName = "affiliationstatus")]
    public string AffiliationStatus { get; set; }

    [JsonProperty(PropertyName = "affiliationmodifytimestamp")]
    public DateTime AffiliationLastModified { get; set; }

    [JsonProperty(PropertyName = "employeeid")]
    public string EmployeeID { get; set; }

    [JsonProperty(PropertyName = "accountstatus")]
    public string AccountStatus { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpiration")]
    public DateTime AccountStatusExpiration { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpmaxdate")]
    public DateTime AccountStatusExpirationMaxDate { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifytimestamp")]
    public DateTime AccountStatusModified { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpnotice")]
    public string AccountStatusExpNotice { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifiedby")]
    public Dictionary<DateTime, string> AccountStatusModifiedBy { get; set; }

    [JsonProperty(PropertyName = "entrycreatedate")]
    public DateTime EntryCreatedate { get; set; }

    [JsonProperty(PropertyName = "entrydeactivationdate")]
    public DateTime EntryDeactivationDate { get; set; }

  }

Et un exemple du JSON à analyser est:

{
    "givenname": [
        "Robert"
    ],
    "passwordexpired": "20091031041550Z",
    "accountstatus": [
        "active"
    ],
    "accountstatusexpiration": [
        "20100612000000Z"
    ],
    "accountstatusexpmaxdate": [
        "20110410000000Z"
    ],
    "accountstatusmodifiedby": {
        "20100214173242Z": "tdecker",
        "20100304003242Z": "jsmith",
        "20100324103242Z": "jsmith",
        "20100325000005Z": "rjones",
        "20100326210634Z": "jsmith",
        "20100326211130Z": "jsmith"
    },
    "accountstatusmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliation": [
        "Employee",
        "Contractor",
        "Staff"
    ],
    "affiliationmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliationstatus": [
        "detached"
    ],
    "entrycreatedate": [
        "20000922072747Z"
    ],
    "username": [
        "rjohnson"
    ],
    "primaryaffiliation": [
        "Staff"
    ],
    "employeeid": [
        "999777666"
    ],
    "sn": [
        "Johnson"
    ]
}
Govind Malviya
la source

Réponses:

76

Avez-vous essayé d'utiliser la méthode générique DeserializeObject?

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

Tous les champs manquants dans les données JSON doivent simplement être laissés NULL.

METTRE À JOUR:

Si la chaîne JSON est un tableau, essayez ceci:

var jarray = JsonConvert.DeserializeObject<List<MyAccount>>(myjsondata);

jarraydevrait alors être un List<MyAccount>.

UNE AUTRE MISE À JOUR:

L'exception que vous obtenez n'est pas cohérente avec un tableau d'objets - je pense que le sérialiseur a des problèmes avec votre accountstatusmodifiedbypropriété de type Dictionary .

Essayez d'exclure la accountstatusmodifiedby propriété de la sérialisation et voyez si cela aide. Si tel est le cas, vous devrez peut-être représenter cette propriété différemment.

Documentation: sérialisation et désérialisation de JSON avec Json.NET

Dave Swersky
la source
Merci. Cependant, j'obtiens une erreur "Impossible de désérialiser le tableau JSON dans le type" System.String "." quand il essaie de désérialiser (par exemple) le tableau JSON givenname dans la chaîne de classe GivenName. Les attributs JSON que j'ai définis comme chaîne dans la classe C # ne sont que des tableaux d'éléments uniques. C'est pourquoi j'ai commencé à choisir les valeurs une par une alors que je rencontrais ce genre de problème pendant le processus de désérialisation. Autre magie que je surplombe?
Donc ... DateTime AccountStatusExpiration(par exemple) n'est pas nullable comme défini dans le code. Que faudrait-il pour le rendre annulable? Changer simplement DateTimeen DateTime??
Hamish Grubijan
50

Réponse reproduite à partir de https://stackoverflow.com/a/10718128/776476

Vous pouvez utiliser le dynamictype C # pour faciliter les choses. Cette technique rend également la refactorisation plus simple car elle ne repose pas sur des chaînes magiques.

Json

La jsonchaîne ci-dessous est une réponse simple à un appel api http et définit deux propriétés: Idet Name.

{"Id": 1, "Name": "biofractal"}

C #

Utilisez JsonConvert.DeserializeObject<dynamic>()pour désérialiser cette chaîne en un type dynamique, puis accédez simplement à ses propriétés de la manière habituelle.

var results = JsonConvert.DeserializeObject<dynamic>(json);
var id = results.Id;
var name= results.Name;

Remarque : Le lien NuGet pour l'assembly NewtonSoft est http://nuget.org/packages/newtonsoft.json . N'oubliez pas d'ajouter: using Newtonsoft.Json;pour accéder à ces classes.

biofractale
la source
7
J'adore l'utilisation de <dynamic>. Cependant, j'ai dû faire ceci pour le faire fonctionner: résultat ["Id"]. Valeur et résultat ["Nom"]. Valeur
fredw
1
Bien que la dynamique soit une bonne alternative, les exemples ci-dessus n'utilisent pas de `` chaînes magiques '' mais utilisent plutôt des génériques fortement typés qui sont mis à jour pendant les techniques de refactoring VS normales (sauf dans une vue dans MVC qui est en dehors du champ d'application de cette question).
gcoleman0828
4
Vous avez toujours des «cordes magiques», elles sont maintenant simplement masquées par l'utilisation de dynamiques!
Ian Ringrose
En effet, la biofractale a le sens des "cordes magiques", comme le souligne @ IanRingrose. L'objet dynamique renvoyé par DeserializeObject<dynamic>n'est par définition pas "vérifié de type". Donc, les lignes suivantes results.Idet results.Namene peuvent pas être vérifiées au moment de la compilation. Toutes les erreurs se produiront au moment de l'exécution. Comparez cela avec un appel fortement typé tel que DeserializeObject<List<MyAccount>>. Les références à ces propriétés peuvent être confirmées au moment de la compilation. L'utilisation de dynamic, bien que pratique, introduit des «chaînes magiques» comme noms de propriété; une réduction de la robustesse à mon humble avis.
ToolmakerSteve
8

Vous pouvez utiliser:

JsonConvert.PopulateObject(json, obj);

ici: jsonest la chaîne json, objest l'objet cible. Voir: exemple

Remarque: PopulateObject()n'effacera pas les données de la liste d'obj, après Populate(), le obj'smembre de la liste contiendra ses données d'origine et les données de la chaîne json

bbants
la source
2
c'est PopulateObject - Populate n'est pas dans le modèle objet.
amok
1
Cela a fonctionné PARFAIT pour moi! J'avais des problèmes avec une chaîne JSON assez complexe, lorsque j'ai essayé de la convertir en objets C #, tout ce qui était marqué comme `` NotNull '' serait absent de l'objet, même s'il était présent dans la chaîne JSON pour commencer. Très étrange. J'ai utilisé cette méthode et cela a fonctionné PARFAIT!
jward01
3

S'appuyant sur la réponse de bbant, c'est ma solution complète pour désérialiser JSON à partir d'une URL distante.

using Newtonsoft.Json;
using System.Net.Http;

namespace Base
{
    public class ApiConsumer<T>
    {
        public T data;
        private string url;

        public CalendarApiConsumer(string url)
        {
            this.url = url;
            this.data = getItems();
        }

        private T getItems()
        {
            T result = default(T);
            HttpClient client = new HttpClient();

            // This allows for debugging possible JSON issues
            var settings = new JsonSerializerSettings
            {
                Error = (sender, args) =>
                {
                    if (System.Diagnostics.Debugger.IsAttached)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                }
            };

            using (HttpResponseMessage response = client.GetAsync(this.url).Result)
            {
                if (response.IsSuccessStatusCode)
                {
                    result = JsonConvert.DeserializeObject<T>(response.Content.ReadAsStringAsync().Result, settings);
                }
            }
            return result;
        }
    }
}

L'utilisation serait comme:

ApiConsumer<FeedResult> feed = new ApiConsumer<FeedResult>("http://example.info/feeds/feeds.aspx?alt=json-in-script");

FeedResultest la classe générée à l'aide du générateur de classes Xamasoft JSON

Voici une capture d'écran des paramètres que j'ai utilisés, permettant des noms de propriétés étranges dont la version Web ne pouvait pas tenir compte.

Générateur de classes Xamasoft JSON

Kyle Falconer
la source
1

J'ai trouvé que j'avais mal construit mon objet. J'ai utilisé http://json2csharp.com/ pour me générer ma classe d'objets à partir du JSON. Une fois que j'ai eu le bon Oject, j'ai pu lancer sans problème. Norbit, erreur de Noob. Je pensais l'ajouter au cas où vous auriez le même problème.

Adam
la source
1

Vous pouvez essayer de vérifier certains des générateurs de classe en ligne pour plus d'informations. Cependant, je pense que certaines des réponses ont été utiles. Voici mon approche qui peut être utile.

Le code suivant a été créé avec une méthode dynamique à l'esprit.

dynObj = (JArray) JsonConvert.DeserializeObject(nvm);

foreach(JObject item in dynObj) {
 foreach(JObject trend in item["trends"]) {
  Console.WriteLine("{0}-{1}-{2}", trend["query"], trend["name"], trend["url"]);
 }
}

Ce code vous permet essentiellement d'accéder aux membres contenus dans la chaîne Json. Juste une manière différente sans avoir besoin des classes. query, trendEt urlsont les objets contenus dans la chaîne JSON.

Vous pouvez également utiliser ce site Web . Ne faites pas confiance aux classes à 100% mais vous voyez l'idée.

Edward Newgate
la source
0

En supposant que vos exemples de données sont corrects, que votre nom donné et les autres entrées entre crochets sont des tableaux dans JS ... vous voudrez utiliser List pour ces types de données. et Liste pour dire accountstatusexpmaxdate ... Je pense que votre exemple a les dates mal formatées, si incertain quant à ce qui est incorrect dans votre exemple.

Ceci est un ancien post, mais je voulais prendre note des problèmes.

Tracker1
la source