28 votos

De conversión personalizada de objetos específicos en JSON.NET

Estoy usando JSON.NET para serializar algunos de mis objetos, y me gustaría saber si hay una manera sencilla para sobrescribir el valor predeterminado json.net convertidor sólo para un objeto específico?

Actualmente tengo la siguiente clase:

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

JSON.NET en la actualidad, se serializa los anteriores como:

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

Es posible que sólo por esa clase específica para el formato de esta manera en su lugar:

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

Yo soy un poco nuevo para JSON.NET.. me preguntaba si el de arriba tiene algo que ver con la escritura de un convertidor personalizado. Yo no era capaz de encontrar ninguna ejemplos concretos sobre cómo escribir una, Si alguien puede me apunte a una fuente específica, que realmente va a apreciar.

Necesito encontrar una solución que hace que la clase específica de convertir siempre el mismo, porque el contexto anterior es parte de un contexto más amplio que el JSON.NET por defecto converter convierte bien.

Espero que mi pregunta es lo suficientemente claro...

ACTUALIZACIÓN:

He encontrado la forma de crear un nuevo convertidor personalizado (mediante la creación de una nueva clase que hereda de JsonConverter y sobrescribir los métodos abstractos), me sobrescribir el WriteJson método de la siguiente manera:

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

Este hecho hace el trabajo correctamente, pero... Estoy intrigado si hay una manera de serializar el resto de las propiedades del objeto mediante la reutilización de los predeterminada JsonSerializer (o convertidor para el caso) en lugar de hacerlo de forma manual "Escribir" el objeto mediante la jsonwriter métodos.

ACTUALIZACIÓN 2: Estoy tratando de conseguir una solución más genérica y se llegó a la siguiente:

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

Esto funciona bien cuando se añade el convertidor de forma manual para el serializador, como este:

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

Pero no funciona cuando se utiliza [JsonConverter()] conjunto de atributos a mi costumbre coverter por encima de la ChannelContext clase debido a una auto-referencia de bucle que se produce cuando la ejecución de:

serializer.Serialize(writer, value)

Esta es, obviamente, porque mi convertidor personalizado ahora se considera el valor predeterminado del convertidor para la clase una vez que se establece con el JsonConverterAttribute, por lo que obtener un inifinite bucle. La única cosa que puedo pensar, con el fin de resolver este problema es la herencia de una base, jsonconverter clase, y las llamadas a la base.serialize() método en lugar de... Pero es un JsonConverter clase, incluso existe?

Muchas gracias!

Mikey

23voto

Mikey S. Puntos 1159

Si alguien está interesado en mi solución:

Cuando serialización ciertas colecciones, quería crear una asociativo json array en lugar de un estándar json array, por lo que mi colega lado del cliente desarrollador puede llegar a los campos de manera eficiente, utilizando su nombre (o la tecla para que la materia) en lugar de iterar a través de ellos.

considere lo siguiente:

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

Respuesta de contexto contiene toda la respuesta que está escrito con el cliente, como se puede ver, se incluye una sección llamada "canales", Y en lugar de producir la channelcontexts en una matriz normal, me gustaría ser capaz de salida de la siguiente manera:

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

Desde quería usar lo anterior para otros contextos, y yo podría decidir el uso de una propiedad diferente, como su "clave", o incluso podría elegir para crear mi propio nombre único, que no tiene que ver con la propiedad, necesitaba algún tipo de una solución genérica, por lo tanto, me escribió una clase genérica llamada AssociativeArraysConverter, que hereda de JsonConverter de la siguiente manera:

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

Y declaró a la siguiente Interfaz:

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

Ahora todo queda por hacer, es crear una clase que implementa IAssociateFieldNameResolver la sola función, que acepta cada elemento de la colección, y devuelve una cadena de caracteres basado en el objeto, que actuará como elemento asociativo del objeto clave.

Ejemplo de una clase es:

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

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: