2012-02-28 23 views
18

Cuando uso Json.Net, entiendo cómo obtener la propiedad $ type en el json representado, pero ¿hay alguna manera de cambiar ese nombre de campo? Necesito usar "__type" en lugar de "$ type".

+0

Necesito esto también, por ejemplo [JSON-LD] (http://json-ld.org) usa '@ type' –

Respuesta

12

Parece que esto está codificado como public const string TypePropertyName = "$type"; en Newtonsoft.Json.Serialization.JsonTypeReflector desafortunadamente, la clase estática interna.

Lo necesitaba yo mismo, y lo único que se me ocurre es tener una versión personalizada modificada de json.net. Lo cual es, por supuesto, una gran pita.

+4

Desafortunadamente, he tenido que abandonar por completo el camino al que me dirigía debido a qué tan difícil es usar Json.Net en muchos casos. Esto es sólo un ejemplo. –

+0

Yo mismo estoy buscando cómo en el lado del servidor, que está escrito i C# y usa Json.NET, puede deserializar JSON enviado desde una aplicación de Android usando Jackson. En Jackson puedo configurar para usar "$ type", pero no funciona de todos modos. – Ted

13

http://json.codeplex.com/workitem/22429

"yo preferiría quedarse con $ Tipo codificado y consistente."

¿De acuerdo con lo que me pregunto?

http://json.codeplex.com/workitem/21989

yo más bien no - creo que esto es demasiado específico para mí y no me quiero ir por la borda con los ajustes. En algún momento probablemente implemente esto - http://json.codeplex.com/workitem/21856 - permitiendo que personas lean/escriban sus propias propiedades meta en el JSON y usted podría volver a implementar el manejo del nombre del tipo con un nuevo nombre de propiedad. La otra opción es solo modificar el código fuente para que usted tenga ese nombre de propiedad.

Esta es mi solución ...

json.Replace("\"$type\": \"", "\"type\": \""); 
+0

¡Su solución es asombrosa!^_^ –

+0

Vea también: https://github.com/JamesNK/Newtonsoft.Json/issues/1331 – manuc66

3

Tuvimos una necesidad para esto, así que creé una costumbre JsonReader. Estamos utilizando el reposo en nuestros servicios web de MS con modelos de datos complejos y necesitamos reemplazar la propiedad "__type" con "$ type".

class MSJsonReader : JsonTextReader 
{ 
    public MSJsonTextReader(TextReader reader) : base(reader) { } 

    public override bool Read() 
    { 
     var hasToken = base.Read(); 

     if (hasToken && base.TokenType == JsonToken.PropertyName && base.Value != null && base.Value.Equals("__type")) 
      base.SetToken(JsonToken.PropertyName, "$type"); 

     return hasToken; 
    } 
} 

Así es como lo utilizamos.

using(JsonReader jr = new MSJsonTextReader(sr)) 
{ 
    JsonSerializer s = new JsonSerializer(); 
    s.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat; 
    s.NullValueHandling = NullValueHandling.Ignore; 
    s.TypeNameHandling = TypeNameHandling.Auto; // Important! 
    s.Binder = new MSRestToJsonDotNetSerializationBinder("Server.DataModelsNamespace", "Client.GeneratedModelsNamespace"); 

    T deserialized = s.Deserialize<T>(jr); 

    return deserialized; 
} 

Aquí es nuestro MSRestToJsonDotNetSerializationBinder que completa la compatibilidad entre la EM descanso y Json.Net.

class MSRestToJsonDotNetSerializationBinder : System.Runtime.Serialization.SerializationBinder 
{ 
    public string ServiceNamespace { get; set; } 
    public string LocalNamespace { get; set; } 

    public MSRestToJsonDotNetSerializationBinder(string serviceNamespace, string localNamespace) 
    { 
     if (serviceNamespace.EndsWith(".")) 
      serviceNamespace = serviceNamespace.Substring(0, -1); 

     if(localNamespace.EndsWith(".")) 
      localNamespace = localNamespace.Substring(0, -1); 

     ServiceNamespace = serviceNamespace; 
     LocalNamespace = localNamespace; 
    } 

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName) 
    { 
     assemblyName = null; 
     typeName = string.Format("{0}:#{1}", serializedType.Name, ServiceNamespace); // MS format 
    } 

    public override Type BindToType(string assemblyName, string typeName) 
    { 
     string jsonDotNetType = string.Format("{0}.{1}", LocalNamespace, typeName.Substring(0, typeName.IndexOf(":#"))); 
     return Type.GetType(jsonDotNetType); 
    } 
} 
1

no hay otra opción que permite serializar tipo personalizado nombre de la propiedad en Json.NET. La idea es no escribir la propiedad predeterminada $type, sino introducir el nombre del tipo como propiedad de la clase.

Supongamos que tenemos una clase Location:

public class Location 
{ 
    public double Latitude { get; set; } 

    public double Longitude { get; set; } 
} 

En primer lugar, es necesario introducir tipo de nombre de la propiedad y modificar la clase como se demuestra a continuación:

public class Location 
{ 
    [JsonProperty("__type")] 
    public string EntityTypeName 
    { 

     get 
     { 
      var typeName = string.Format("{0}, {1}", GetType().FullName, GetType().Namespace); 
      return typeName; 
     } 
    } 

    public double Latitude { get; set; } 

    public double Longitude { get; set; } 
} 

A continuación, establezca JsonSerializerSettings.TypeNameHandling-TypeNameHandling.None para que el deserializador omita la representación del atributo predeterminado $type.

Eso es todo.

Ejemplo

var point = new Location() { Latitude = 51.5033630, Longitude = -0.1276250 }; 

var jsonLocation = JsonConvert.SerializeObject(point, new JsonSerializerSettings 
{ 
    TypeNameHandling = TypeNameHandling.None, //do not write type property(!) 
}); 
Console.WriteLine(jsonLocation); 

Resultado

{"__type":"Namespace.Location, Namespace","Latitude":51.503363,"Longitude":-0.127625} 
+0

No veo cómo deserializar determinará el tipo correcto con su solución. ¿Puedes explicar? –

+0

Para deserializar, intente con 'JsonCreationConverter', p. ver http://stackoverflow.com/questions/8030538 – xmedeko

2

al serializar, no es una buena manera de anular el nombre de la propiedad:

public class CustomJsonWriter : JsonTextWriter 
{ 
    public CustomJsonWriter(TextWriter writer) : base(writer) 
    { 
    } 

    public override void WritePropertyName(string name, bool escape) 
    { 
     if (name == "$type") name = "__type"; 
     base.WritePropertyName(name, escape); 
    } 
} 

var serializer = new JsonSerializer(); 
var writer = new StreamWriter(stream) { AutoFlush = true }; 
serializer.Serialize(new CustomJsonWriter(writer), objectToSerialize); 

No he probado deserialización aún, pero en peor de los casos que podría utilizar:

json.Replace("\"__type": \"", "\"type\": \"$type\"); 
2

que tenía que hacer esto para mi interfaz de usuario API REST como nombres angularjs disregards campos comenzando con un signo de dólar ($).

Así que aquí es una solución que cambia el nombre de $type-__type para toda la Web API y trabaja tanto para la serialización y deserialización.

Con el fin de poder utilizar una costumbre JsonWriter y una costumbre JsonReader (como se propone en las otras respuestas a esta pregunta), tenemos que heredar el JsonMediaTypeFormatter y anular los métodos correspondientes:

internal class CustomJsonNetFormatter : JsonMediaTypeFormatter 
{ 
    public override JsonReader CreateJsonReader(Type type, Stream readStream, Encoding effectiveEncoding) 
    { 
     return new CustomJsonReader(readStream, effectiveEncoding); 
    } 

    public override JsonWriter CreateJsonWriter(Type type, Stream writeStream, Encoding effectiveEncoding) 
    { 
     return new CustomJsonWriter(writeStream, effectiveEncoding); 
    } 

    private class CustomJsonWriter : JsonTextWriter 
    { 
     public CustomJsonWriter(Stream writeStream, Encoding effectiveEncoding) 
      : base(new StreamWriter(writeStream, effectiveEncoding)) 
     { 
     } 

     public override void WritePropertyName(string name, bool escape) 
     { 
      if (name == "$type") name = "__type"; 
      base.WritePropertyName(name, escape); 
     } 
    } 

    private class CustomJsonReader : JsonTextReader 
    { 
     public CustomJsonReader(Stream readStream, Encoding effectiveEncoding) 
      : base(new StreamReader(readStream, effectiveEncoding)) 
     { 
     } 

     public override bool Read() 
     { 
      var hasToken = base.Read(); 
      if (hasToken && TokenType == JsonToken.PropertyName && Value != null && Value.Equals("__type")) 
      { 
       SetToken(JsonToken.PropertyName, "$type"); 
      } 
      return hasToken; 
     } 
    } 
} 

De Por supuesto, debe registrar el formateador personalizado en su WebApiConfig. Así que reemplazamos el formateador Json.NET predeterminado por uno personalizado:

config.Formatters.Remove(config.Formatters.JsonFormatter); 
config.Formatters.Add(new CustomJsonNetFormatter()); 

Listo.

0

También podría hacerlo de esta manera:

[JsonConverter(typeof(JsonSubtypes), "ClassName")] 
public class Annimal 
{ 
    public virtual string ClassName { get; } 
    public string Color { get; set; } 
} 

Tendrá el convertidor JsonSubtypes que no es parte de Newtonsoft.Json proyecto.

Cuestiones relacionadas