c# serialization json.net

c# - Serializar solo propiedades de interfaz a JSON con Json.net



serialization (9)

Versión mejorada con interfaces anidadas + soporte para objetos xsd.exe

Otra variación más aquí. El código provino de tomdupont.net/2015/09/how-to-only-serialize-interface.html con las siguientes mejoras sobre otras respuestas aquí

  • Maneja la jerarquía, por lo que si tiene una Interface2[] dentro de una Interface1 , se serializará.
  • Intentaba serializar un objeto proxy de WCF y el JSON resultante apareció como {} . Resultó que todas las propiedades se establecieron en Ignore=true así que tuve que agregar un bucle para configurarlas todas para que no se ignoren.

    public class InterfaceContractResolver : DefaultContractResolver { private readonly Type[] _interfaceTypes; private readonly ConcurrentDictionary<Type, Type> _typeToSerializeMap; public InterfaceContractResolver(params Type[] interfaceTypes) { _interfaceTypes = interfaceTypes; _typeToSerializeMap = new ConcurrentDictionary<Type, Type>(); } protected override IList<JsonProperty> CreateProperties( Type type, MemberSerialization memberSerialization) { var typeToSerialize = _typeToSerializeMap.GetOrAdd( type, t => _interfaceTypes.FirstOrDefault( it => it.IsAssignableFrom(t)) ?? t); var props = base.CreateProperties(typeToSerialize, memberSerialization); // mark all props as not ignored foreach (var prop in props) { prop.Ignored = false; } return props; } }

Con una clase / interfaz simple como esta

public interface IThing { string Name { get; set; } } public class Thing : IThing { public int Id { get; set; } public string Name { get; set; } }

¿Cómo puedo obtener la cadena JSON con solo la propiedad "Nombre" (solo las propiedades de la interfaz subyacente)?

En realidad, cuando hago eso:

var serialized = JsonConvert.SerializeObject((IThing)theObjToSerialize, Formatting.Indented); Console.WriteLine(serialized);

Obtengo el objeto completo como JSON (Id + Nombre);



El método que uso,

public class InterfaceContractResolver : DefaultContractResolver { private readonly Type _InterfaceType; public InterfaceContractResolver (Type InterfaceType) { _InterfaceType = InterfaceType; } protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { //IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization); IList<JsonProperty> properties = base.CreateProperties(_InterfaceType, memberSerialization); return properties; } } // To serialize do this: var settings = new JsonSerializerSettings() { ContractResolver = new InterfaceContractResolver (typeof(IThing)) }); string json = JsonConvert.SerializeObject(theObjToSerialize, settings);


Finalmente recibí cuando no funcionaría ... Si quieres tener dentro de otro objeto complejo, no se serializará correctamente.

Así que hice una versión que extraerá solo los datos almacenados en un ensamblaje específico y para los tipos que tienen la misma interfaz base.

Por lo tanto, está hecho como .Net Core JsonContractResolver.

Además de la extracción de datos, resuelve:
a) conversión de camelCase antes de enviar datos al cliente
b) utiliza la interfaz más alta desde el alcance permitido (por ensamblaje) c) arregla el orden de los campos: el campo de la mayoría de la clase base se enumerará primero y el objeto anidado también cumplirá con esta regla.

public class OutputJsonResolver : DefaultContractResolver { #region Static Members private static readonly object syncTargets = new object(); private static readonly Dictionary<Type, IList<JsonProperty>> Targets = new Dictionary<Type, IList<JsonProperty>>(); private static readonly Assembly CommonAssembly = typeof(ICommon).Assembly; #endregion #region Override Members protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { if (type.Assembly != OutputJsonResolver.CommonAssembly) return base.CreateProperties(type, memberSerialization); IList<JsonProperty> properties; if (OutputJsonResolver.Targets.TryGetValue(type, out properties) == false) { lock (OutputJsonResolver.syncTargets) { if (OutputJsonResolver.Targets.ContainsKey(type) == false) { properties = this.CreateCustomProperties(type, memberSerialization); OutputJsonResolver.Targets[type] = properties; } } } return properties; } protected override string ResolvePropertyName(string propertyName) { return propertyName.ToCase(Casing.Camel); } #endregion #region Assistants private IList<JsonProperty> CreateCustomProperties(Type type, MemberSerialization memberSerialization) { // Hierarchy IReadOnlyList<Type> types = this.GetTypes(type); // Head Type head = types.OrderByDescending(item => item.GetInterfaces().Length).FirstOrDefault(); // Sources IList<JsonProperty> sources = base.CreateProperties(head, memberSerialization); // Targets IList<JsonProperty> targets = new List<JsonProperty>(sources.Count); // Repository IReadOnlyDistribution<Type, JsonProperty> repository = sources.ToDistribution(item => item.DeclaringType); foreach (Type current in types.Reverse()) { IReadOnlyPage<JsonProperty> page; if (repository.TryGetValue(current, out page) == true) targets.AddRange(page); } return targets; } private IReadOnlyList<Type> GetTypes(Type type) { List<Type> types = new List<Type>(); if (type.IsInterface == true) types.Add(type); types.AddRange(type.GetInterfaces()); return types; } #endregion }


Inspirado por @ user3161686, aquí hay una pequeña modificación de InterfaceContractResolver :

public class InterfaceContractResolver<TInterface> : DefaultContractResolver where TInterface : class { protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { IList<JsonProperty> properties = base.CreateProperties(typeof(TInterface), memberSerialization); return properties; } }


Me gustaría compartir lo que terminamos haciendo cuando nos enfrentamos con esta tarea. Dada la interfaz y clase de OP ...

public interface IThing { string Name { get; set; } } public class Thing : IThing { public int Id { get; set; } public string Name { get; set; } }

... creamos una clase que es la implementación directa de la interfaz ...

public class DirectThing : IThing { public string Name { get; set; } }

Luego simplemente serializó nuestra instancia Thing , la deserializó como DirectThing y luego la serializó como DirectThing :

var thing = new Thing(); JsonConvert.SerializeObject( JsonConvert.DeserializeObject<DirectThing>(JsonConvert.SerializeObject(thing)));

Este enfoque puede funcionar con una larga cadena de herencia de interfaz ... solo necesita hacer una clase directa ( DirectThing en este ejemplo) al nivel de interés. No hay necesidad de preocuparse por la reflexión o los atributos.

Desde una perspectiva de mantenimiento, la clase DirectThing es fácil de mantener si agrega miembros a IThing porque el compilador dará errores si no los ha puesto también en DirectThing . Sin embargo, si elimina un miembro X de IThing y lo coloca en Thing , tendrá que acordarse de eliminarlo de DirectThing o de lo contrario X estaría en el resultado final.

Desde la perspectiva del rendimiento, hay tres (de) operaciones de serialización que suceden aquí en lugar de una, por lo que dependiendo de su situación, es posible que desee evaluar la diferencia de rendimiento de las soluciones basadas en reflector / atributo frente a esta solución. En mi caso, solo estaba haciendo esto en pequeña escala, por lo que no me preocupaban las pérdidas potenciales de algunos micro / milisegundos.

Espero que ayude a alguien!


Puede agregar la anotación [JsonIgnore] para ignorar un atributo.


Puede usar la serialización condicional. Eche un vistazo a este link . Básicamente, debe implementar la interfaz IContractResolver , sobrecargar el método ShouldSerialize y pasar su resolver al constructor del Serializador Json.


Una alternativa a [JsonIgnore] son los atributos [DataContract] y [DataMember] . Si su clase está etiquetada con [DataContract] el serializador solo procesará las propiedades etiquetadas con el atributo [DataMember] ( JsonIgnore es un modelo de "exclusión JsonIgnore " mientras que DataContract es "op-in").

[DataContract] public class Thing : IThing { [DataMember] public int Id { get; set; } public string Name { get; set; } }

La limitación de ambos enfoques es que deben implementarse en la clase, no puede agregarlos a la definición de la interfaz.