2011-06-19 20 views
31

Estoy usando JSON.NET para serializar algunos de mis objetos, y me gustaría saber si hay una manera simple de reemplazar el convertidor json.net predeterminado solo para un objeto específico?Conversión personalizada de objetos específicos en JSON.NET

Actualmente tengo la clase siguiente:

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

JSON.NET serializa actualmente lo anterior como:

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

¿Es posible sólo para esa clase específica para formatearlo la manera siguiente:

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

Soy un poco nuevo en JSON.NET .. Me preguntaba si lo anterior tiene algo que d o con la escritura de un convertidor personalizado. No pude encontrar ningún ejemplo concreto sobre cómo escribir uno. Si alguien puede indicarme una fuente específica, realmente lo agradeceré.

Necesito encontrar una solución que haga que esa clase específica siempre convierta la misma, porque el contexto anterior es parte de un contexto aún mayor que el convertidor predeterminado JSON.NET convierte muy bien.

espero que mi pregunta es lo suficientemente claro ...

ACTUALIZACIÓN:

he descubierto cómo crear un nuevo convertidor de medida (mediante la creación de una nueva clase que hereda de JsonConverter y anular es abstracta métodos), que overriden el método WriteJson 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(); 
    } 

de hecho, esto hace el trabajo con éxito, pero ... me intriga si hay una manera de serializar el r Est de las propiedades del objeto mediante la reutilización del JsonSerializer predeterminado (o convertidor para el caso) en lugar de manualmente "Escribir" el objeto utilizando los métodos jsonwriter.

ACTUALIZACIÓN 2: que estoy tratando de conseguir una solución más genérica y se le ocurrió 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, así:

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

pero no funciona cuando se utiliza el [JsonConverter()] conjunto de atributos a mi costumbre Coverter encima de la clase ChannelContext debido a un bucle de referencia de auto que se produce cuando se ejecuta:

serializer.Serialize(writer, value) 

Esto es obviamente porque mi convertidor de encargo se considera ahora el convertidor predeterminado para la clase una vez que se establece con el JsonConverterAttribute, por lo que obtener un bucle inifinite. Lo único que se me ocurre, para resolver este problema es heredar de una clase básica, jsonconverter, y llamar al método base.serialize() en su lugar ... ¿Pero existe una clase JsonConverter tal?

¡Muchas gracias!

Mikey

+0

+1 para encontrar la solución. –

Respuesta

28

Si alguien está interesado en mi solución:

Cuando seria ciertas colecciones, quería crear una matriz JSON asociativo en lugar de una matriz JSON estándar, por lo que mi colega desarrollador lado del cliente puede llegar a los campos de manera eficiente, usando su nombre (o clave para ese asunto) en lugar de iterar a través de ellos.

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

contexto de respuesta contiene toda la respuesta que se escribe de nuevo al cliente, como se puede ver, que incluye una sección llamada "canales", y en lugar de dar salida a los 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 ] 
} 
} 

como quería utilizar lo anterior para otros contextos, y podría decidir utilizar una propiedad diferente como su " clave ", o incluso puede elegir crear mi propio nombre único, que no tiene que ver con la propiedad, que necesitaba algún tipo de una solución genérica, por lo tanto, escribí 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(); 
    } 
} 

fue declarado la siguiente interfaz:

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

Ahora todo lo que queda por hacer, es crear una clase que implementa la función única de IAssociateFieldNameResolver, que acepta cada elemento de la colección, y devuelve una cadena con base en ese objeto, que actuará como la clave del objeto asociativo del elemento.

Ejemplo para una clase de este tipo es:

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

¿Cómo evitó proporcionar una implementación para ReadJson al anular JsonConverter? –

Cuestiones relacionadas