28 votes

La coutume de conversion des objets spécifiques dans JSON.NET

Je suis à l'aide d'JSON.NET pour sérialiser certains de mes objets, et je voudrais savoir si il existe un moyen simple de remplacer la valeur par défaut json.net convertisseur seulement pour un objet spécifique?

Actuellement, j'ai la classe suivante:

public class ChannelContext : IDataContext
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<INewsItem> Items { get; set; }
}

JSON.NET actuellement sérialise le dessus comme:

{
    "Id": 2,
    "Name": "name value",
    "Items": [ item_data_here ]
}

Est-il possible juste pour cette classe spécifique pour le format de cette façon au lieu de cela:

"Id_2":
{
    "Name": "name value",
    "Items": [ item data here ]
}

Je suis un peu nouveau pour JSON.NET.. je me demandais si ce qui précède a quelque chose à voir avec la rédaction d'un convertisseur personnalisé. Je n'étais pas en mesure de trouver des exemples concrets sur la façon d'écrire une, Si quelqu'un peut me pointer à une source spécifique, je vais vraiment l'apprécier.

J'ai besoin de trouver une solution, ce qui rend cette classe spécifique convertissez toujours la même, parce que le contexte ci-dessus est une partie d'un même contexte plus large qui l'JSON.NET par défaut convertisseur convertit l'amende juste.

Espère que ma question est assez claire...

Mise à JOUR:

J'ai trouvé comment créer un nouveau convertisseur personnalisé (par la création d'une nouvelle classe qui hérite de JsonConverter et l'écraser de méthodes abstraites), j'ai remplacer le WriteJson méthode comme suit:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        ChannelContext contextObj = value as ChannelContext;

        writer.WriteStartObject();
        writer.WritePropertyName("id_" + contextObj.Id);
        writer.WriteStartObject();
        writer.WritePropertyName("Name");
        serializer.Serialize(writer, contextObj.Name);

        writer.WritePropertyName("Items");
        serializer.Serialize(writer, contextObj.Items);
        writer.WriteEndObject();
        writer.WriteEndObject();
    }

Ce, en effet, que le travail avec succès, mais... Je me demande si il existe un moyen de sérialiser le reste des propriétés de l'objet par la réutilisation de la valeur par défaut JsonSerializer (ou convertisseur d'ailleurs) au lieu de saisir manuellement "l'Écriture" de l'objet à l'aide de la jsonwriter méthodes.

Mise à JOUR 2: Je vais essayer d'obtenir un plus solution générique et est venu avec les éléments suivants:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteStartObject();

        // Write associative array field name
        writer.WritePropertyName(m_FieldNameResolver.ResolveFieldName(value));

        // Remove this converter from serializer converters collection
        serializer.Converters.Remove(this);

        // Serialize the object data using the rest of the converters
        serializer.Serialize(writer, value);

        writer.WriteEndObject();
    }

Cela fonctionne bien lors de l'ajout de l'converter manuellement pour le sérialiseur, comme ceci:

jsonSerializer.Converters.Add(new AssociativeArraysConverter<DefaultFieldNameResolver>());
jsonSerializer.Serialize(writer, channelContextObj);

Mais ne fonctionne pas lors de l'utilisation de la [JsonConverter()] attribut défini pour mon custom coverter au-dessus de la ChannelContext la classe à cause d'un soi boucle de référence qui se produit lors de l'exécution:

serializer.Serialize(writer, value)

C'est évidemment parce que mon convertisseur personnalisé est maintenant considéré comme la valeur par défaut du convertisseur pour la classe une fois qu'il est réglé par le JsonConverterAttribute, si je reçois un inifinite boucle. La seule chose à laquelle je pense, dans le but de résoudre ce problème est d'hériter de base, jsonconverter de classe, et l'appel de la base.méthode de serialize() à la place... Mais ce JsonConverter classe existe encore?

Merci beaucoup!

Mikey

23voto

Mikey S. Points 1159

Si ça intéresse quelqu'un dans ma solution:

Lors de la sérialisation de certaines collections, j'ai voulu créer un cadre associatif tableau json au lieu d'un standard tableau json, donc, mon collègue côté client développeur peut atteindre ces champs de manière efficace, en utilisant leur nom (ou clé) à la place de l'itération à travers eux.

considérez les points suivants:

public class ResponseContext
{
    private List<ChannelContext> m_Channels;

    public ResponseContext()
    {
        m_Channels = new List<ChannelContext>();
    }

    public HeaderContext Header { get; set; }

    [JsonConverter(
        typeof(AssociativeArraysConverter<ChannelContextFieldNameResolver>))]
    public List<ChannelContext> Channels
    {
        get { return m_Channels; }
    }

}

[JsonObject(MemberSerialization = MemberSerialization.OptOut)]
public class ChannelContext : IDataContext
{
    [JsonIgnore]
    public int Id { get; set; }

    [JsonIgnore]
    public string NominalId { get; set; }

    public string Name { get; set; }

    public IEnumerable<Item> Items { get; set; }
}

Réponse de contexte contient l'ensemble de la réponse qui est écrit au client, comme vous pouvez le voir, il comporte une section intitulée "canaux", Et au lieu de sortir le channelcontexts dans un tableau normal, j'aimerais être en mesure de produire de la manière suivante:

"Channels"
{
"channelNominalId1":
{
  "Name": "name value1"
  "Items": [ item data here ]
},
"channelNominalId2":
{
  "Name": "name value2"
  "Items": [ item data here ]
}
}

Depuis que j'ai voulu utiliser la ci-dessus pour d'autres contextes, et je pourrais décider d'utiliser une autre propriété que leur "clé", ou pourrait même choisir de créer mon propre nom, ce qui ne veut pas avoir à faire avec tout bien, j'ai besoin d'une sorte de une solution générique, donc j'ai écrit une classe générique appelé AssociativeArraysConverter, qui hérite de l'JsonConverter de la manière suivante:

public class AssociativeArraysConverter<T> : JsonConverter
    where T : IAssociateFieldNameResolver, new()
{
    private T m_FieldNameResolver;

    public AssociativeArraysConverter()
    {
        m_FieldNameResolver = new T();
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(IEnumerable).IsAssignableFrom(objectType) &&
                !typeof(string).IsAssignableFrom(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        IEnumerable collectionObj = value as IEnumerable;

        writer.WriteStartObject();

        foreach (object currObj in collectionObj)
        {
            writer.WritePropertyName(m_FieldNameResolver.ResolveFieldName(currObj));
            serializer.Serialize(writer, currObj);
        }

        writer.WriteEndObject();
    }
}

Et il a déclaré l'Interface suivante:

public interface IAssociateFieldNameResolver
{
    string ResolveFieldName(object i_Object);
}

Maintenant, tout reste à faire, c'est de créer une classe qui implémente IAssociateFieldNameResolver unique fonction, qui accepte, chaque élément de la collection, et retourne une chaîne de caractères à partir de cet objet, qui sera l'objet associatif de l'objet clé.

Exemple pour une telle classe est:

public class ChannelContextFieldNameResolver : IAssociateFieldNameResolver
{
    public string ResolveFieldName(object i_Object)
    {
        return (i_Object as ChannelContext).NominalId;
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X