newtonsoft - serializar json c#
Excluir propiedad de la serialización mediante el atributo personalizado(json.net) (6)
Aquí hay un método basado en el excelente contrato serializador de drzaus que usa expresiones lambda. Simplemente agréguelo a la misma clase. Después de todo, ¿quién no prefiere que el compilador haga la comprobación por ellos?
public IgnorableSerializerContractResolver Ignore<TModel>(Expression<Func<TModel, object>> selector)
{
MemberExpression body = selector.Body as MemberExpression;
if (body == null)
{
UnaryExpression ubody = (UnaryExpression)selector.Body;
body = ubody.Operand as MemberExpression;
if (body == null)
{
throw new ArgumentException("Could not get property name", "selector");
}
}
string propertyName = body.Member.Name;
this.Ignore(typeof (TModel), propertyName);
return this;
}
Ahora puede ignorar las propiedades de manera fácil y fluida:
contract.Ignore<Node>(node => node.NextNode)
.Ignore<Node>(node => node.AvailableNodes);
Necesito poder controlar cómo / si ciertas propiedades en una clase son serializadas. El caso más simple es [ScriptIgnore]
. Sin embargo, solo quiero que se cumplan estos atributos para esta situación de serialización específica en la que estoy trabajando: si otros módulos de la aplicación también quieren serializar estos objetos, ninguno de estos atributos debería interferir.
Así que mi idea es usar un atributo personalizado MyAttribute
en las propiedades e inicializar la instancia específica de JsonSerializer con un gancho que sepa buscar ese atributo.
A primera vista, no veo ninguno de los puntos de enlace disponibles en JSON.NET proporcionará el PropertyInfo
para la propiedad actual para realizar dicha inspección, solo el valor de la propiedad. ¿Me estoy perdiendo de algo? ¿O una mejor manera de abordar esto?
Aquí hay una resolución genérica reutilizable de "ignorar propiedad" basada en la respuesta aceptada :
/// <summary>
/// Special JsonConvert resolver that allows you to ignore properties. See https://.com/a/13588192/1037948
/// </summary>
public class IgnorableSerializerContractResolver : DefaultContractResolver {
protected readonly Dictionary<Type, HashSet<string>> Ignores;
public IgnorableSerializerContractResolver() {
this.Ignores = new Dictionary<Type, HashSet<string>>();
}
/// <summary>
/// Explicitly ignore the given property(s) for the given type
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName">one or more properties to ignore. Leave empty to ignore the type entirely.</param>
public void Ignore(Type type, params string[] propertyName) {
// start bucket if DNE
if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet<string>();
foreach (var prop in propertyName) {
this.Ignores[type].Add(prop);
}
}
/// <summary>
/// Is the given property for the given type ignored?
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public bool IsIgnored(Type type, string propertyName) {
if (!this.Ignores.ContainsKey(type)) return false;
// if no properties provided, ignore the type entirely
if (this.Ignores[type].Count == 0) return true;
return this.Ignores[type].Contains(propertyName);
}
/// <summary>
/// The decision logic goes here
/// </summary>
/// <param name="member"></param>
/// <param name="memberSerialization"></param>
/// <returns></returns>
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (this.IsIgnored(property.DeclaringType, property.PropertyName)
// need to check basetype as well for EF -- @per comment by user576838
|| this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)) {
property.ShouldSerialize = instance => { return false; };
}
return property;
}
}
Y el uso:
var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };
No me importa establecer los nombres de las propiedades como cadenas, en caso de que alguna vez cambien, rompería mi otro código.
Tenía varios "modos de visualización" en los objetos que necesitaba serializar, así que terminé haciendo algo como esto en el resolver del contrato (modo de vista proporcionado por el argumento del constructor):
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (viewMode == ViewModeEnum.UnregisteredCustomer && member.GetCustomAttributes(typeof(UnregisteredCustomerAttribute), true).Length == 0)
{
property.ShouldSerialize = instance => { return false; };
}
return property;
}
Donde mis objetos se ven así:
public interface IStatement
{
[UnregisteredCustomer]
string PolicyNumber { get; set; }
string PlanCode { get; set; }
PlanStatus PlanStatus { get; set; }
[UnregisteredCustomer]
decimal TotalAmount { get; }
[UnregisteredCustomer]
ICollection<IBalance> Balances { get; }
void SetBalances(IBalance[] balances);
}
La desventaja de esto sería un poco de reflexión en el resolver, pero creo que vale la pena tener un código más fácil de mantener.
Tienes pocas opciones. Le recomiendo que lea el artículo de la documentación de Json.Net sobre el tema antes de leer a continuación.
El artículo presenta dos métodos:
- Cree un método que devuelva un valor de
bool
basado en una convención de nomenclatura que Json.Net seguirá para determinar si serializar o no la propiedad. - Cree una resolución de contrato personalizada que ignore la propiedad.
De los dos, yo prefiero este último. Omitir atributos por completo: solo utilícelos para ignorar propiedades en todas las formas de serialización. En su lugar, cree una resolución de contrato personalizada que ignore la propiedad en cuestión, y solo use la resolución de contrato cuando desee ignorar la propiedad, dejando a otros usuarios de la clase libres para serializar la propiedad o no a su antojo.
Editar Para evitar link rot, estoy publicando el código en cuestión del artículo
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance =
new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty( MemberInfo member,
MemberSerialization memberSerialization )
{
JsonProperty property = base.CreateProperty( member, memberSerialization );
if( property.DeclaringType == typeof(Employee) &&
property.PropertyName == "Manager" )
{
property.ShouldSerialize = instance =>
{
// replace this logic with your own, probably just
// return false;
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
Tuve buenos resultados con la combinación de las respuestas drzaus y Steve Rukuts. Sin embargo, tengo un problema cuando configuro JsonPropertyAttribute con un nombre o límites diferentes para la propiedad. Por ejemplo:
[JsonProperty("username")]
public string Username { get; set; }
Incluir UnderlyingName en consideración resuelve el problema:
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (this.IsIgnored(property.DeclaringType, property.PropertyName)
|| this.IsIgnored(property.DeclaringType, property.UnderlyingName)
|| this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)
|| this.IsIgnored(property.DeclaringType.BaseType, property.UnderlyingName))
{
property.ShouldSerialize = instance => { return false; };
}
return property;
}
Use el atributo JsonIgnore
.
Por ejemplo, para excluir Id
:
public class Person {
[JsonIgnore]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}