web-dev-qa-db-fra.com

Sérialisation Json personnalisée de la classe

J'ai un code structuré comme ci-dessous.

public class Stats
{
        public string URL { get; set; }
        public string Status { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public int Length { get; set; }
}

et

 public class UrlStats
 {
        public string URL { get; set; }
        public int TotalPagesFound { get; set; }
        public List<Stats> TotalPages { get; set; }
        public int TotalTitleTags { get; set; }
        public List<Stats> TotalTitles { get; set; }
        public int NoDuplicateTitleTags { get; set; }
        public List<Stats> DuplicateTitles { get; set; }
        public int NoOverlengthTitleTags { get; set; }
        public List<Stats> OverlengthTitles { get; set; }
 }

Fondamentalement, je scanne un site Web pour des statistiques comme les balises de titre, les titres en double, etc.

J'utilise JQuery et je fais AJAX appels au service Web et je récupère les statistiques d'url pendant que le processus est en cours d'exécution pour afficher les statistiques d'url des utilisateurs de loin collectées car il faut beaucoup de temps pour analyser un grand site Web. Donc après toutes les 5 secondes, je récupère les statistiques du serveur. Maintenant, le problème est toutes les données variables de liste que je dois envoyer à la fin lorsque le traitement de l'analyse est terminée, pas pendant les mises à jour. Ce qui se passe en ce moment, les données variables List<Stats> sont également envoyé pendant les mises à jour qui est un gros morceau de données et je veux envoyer uniquement les variables de type int qui sont nécessaires pour afficher les mises à jour du processus.

De la recherche sur Internet, je n'ai rien trouvé d'utile pour résoudre mon problème et j'ai trouvé que Json.NET est une très bonne bibliothèque mais je ne sais vraiment pas comment l'utiliser correctement pour obtenir ce que je veux.

Fondamentalement, je recherche des propriétés de sérialisation en fonction de leur type de données au moment de l'exécution, si c'est possible.

23
Dinesh Ahuja

Il existe deux approches différentes pour votre problème.

Vous devez choisir la première si vous voulez changer vos classes plus souvent car la première approche vous évite d'oublier de sérialiser une propriété nouvellement ajoutée. De plus, il est beaucoup plus réutilisable si vous souhaitez ajouter d'autres classes que vous souhaitez sérialiser de la même manière.

Si vous ne disposez que de ces deux classes et qu'il est très probable qu'elles ne changeront pas, vous pouvez choisir la deuxième approche pour garder votre solution simple.

1. Utilisez un convertisseur personnalisé pour sélectionner toutes les propriétés int

La première approche consiste à utiliser un JsonConverter personnalisé qui sérialise une classe ou une structure en n'incluant que les propriétés de type int. Le code pourrait ressembler à ceci:

class IntPropertyConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        // this converter can be applied to any type
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // we currently support only writing of JSON
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            serializer.Serialize(writer, null);
            return;
        }

        // find all properties with type 'int'
        var properties = value.GetType().GetProperties().Where(p => p.PropertyType == typeof(int));

        writer.WriteStartObject();

        foreach (var property in properties)
        {
            // write property name
            writer.WritePropertyName(property.Name);
            // let the serializer serialize the value itself
            // (so this converter will work with any other type, not just int)
            serializer.Serialize(writer, property.GetValue(value, null));
        }

        writer.WriteEndObject();
    }
}

Ensuite, vous devez décorer votre classe avec un JsonConverterAttribute:

[JsonConverter(typeof(IntPropertyConverter))]
public class UrlStats
{
    // ...
}

Avertissement: Ce code n'a été testé que très approximativement.


2. Choisissez les propriétés individuellement

La deuxième solution semble un peu plus simple: vous pouvez utiliser le JsonIgnoreAttribute pour décorer les attributs que vous souhaitez exclure pour la sérialisation. Alternativement, vous pouvez passer de la "liste noire" à la "liste blanche" en incluant explicitement les attributs que vous souhaitez sérialiser. Voici un exemple de code:

Blacklisting: (J'ai réorganisé les propriétés pour une meilleure vue d'ensemble)

[JsonObject(MemberSerialization.OptOut)] // this is default and can be omitted
public class UrlStats
{
    [JsonIgnore] public string URL { get; set; }
    [JsonIgnore] public List<Stats> TotalPages { get; set; }
    [JsonIgnore] public List<Stats> TotalTitles { get; set; }
    [JsonIgnore] public List<Stats> DuplicateTitles { get; set; }
    [JsonIgnore] public List<Stats> OverlengthTitles { get; set; }

    public int TotalPagesFound { get; set; }
    public int TotalTitleTags { get; set; }
    public int NoDuplicateTitleTags { get; set; }
    public int NoOverlengthTitleTags { get; set; }
}

Liste blanche: (également réorganisé)

[JsonObject(MemberSerialization.OptIn)] // this is important!
public class UrlStats
{
    public string URL { get; set; }
    public List<Stats> TotalPages { get; set; }
    public List<Stats> TotalTitles { get; set; }
    public List<Stats> DuplicateTitles { get; set; }
    public List<Stats> OverlengthTitles { get; set; }

    [JsonProperty] public int TotalPagesFound { get; set; }
    [JsonProperty] public int TotalTitleTags { get; set; }
    [JsonProperty] public int NoDuplicateTitleTags { get; set; }
    [JsonProperty] public int NoOverlengthTitleTags { get; set; }
}
31
fero

Oh j'ai compris, relisant votre question, je pense que vous pouvez sérialiser une projection de vos données.

Vous pouvez essayer ce qui suit:

var json = JsonConvert.SerializeObject(new { u.TotalPagesFound, u.TotalTitleTags, u.NoDuplicateTitleTags, u.NoOverlengthTitleTags } );

Cela convertira en JSON uniquement les propriétés int de votre classe. C'est la manière la plus simple et elle est liée à la structure de votre classe. Si vous voulez quelque chose de plus général, vous devrez implémenter un convertisseur personnalisé.

Réponse originale:

Je ne vois aucun problème avec vos classes, vous n'avez rien de bizarre comme les références de boucle, donc Json.NET ne devrait avoir aucun problème à sérialiser votre classe. Alors allez chercher Json.NET et vous pouvez essayer ce qui suit

// create new instance of your url stat class
var u = new UrlStats() { URL = "a.com", TotalPages = new List<Stats>() { new Stats() { URL = "b.com", Status = "xxxx" } } };
// seralize!
var json = JsonConvert.SerializeObject(u);
Console.Write(json);

Ce que j'obtiens avec cette méthode est quelque chose comme ceci:

{"URL":"a.com","TotalPagesFound":0,"TotalPages":[{"URL":"b.com","Status":"xxxx","Title":null,"Description":null,"Length":0}],"TotalTitleTags":0,"TotalTitles":null,"NoDuplicateTitleTags":0,"DuplicateTitles":null,"NoOverlengthTitleTags":0,"OverlengthTitles":null}

Et cela me semble bon.

2
corrego