c# - serialize - Configure JSON.NET para ignorar los atributos DataContract/DataMember
Nos encontramos con una situación en un proyecto de MVC3 con los serializadores JSON de Microsoft y JSON.NET.
Todo el mundo sabe que DateTime está básicamente roto en los serializadores de Microsoft, así que cambiamos a JSON.NET para evitar este problema. Eso funciona muy bien, excepto que algunas de las clases que intentamos serializar son POCO con atributos DataContract / DataMember. Se definen en un conjunto al que se hace referencia en múltiples lugares. Además, tienen algunas otras propiedades de visualización que no están marcadas como DataMembers para mayor eficiencia. Por ejemplo, un cliente
[DataContract]
public class Customer
{
[DataMember]
public string FirstName { get; set;}
[DataMember]
public string LastName { get; set;}
public string FullName
{
get
{ return FirstName + " " + LastName; }
}
}
Cuando este cliente pasa sobre WCF, el lado del cliente puede hacer referencia a ese ensamblado y usar FullName, pero cuando se serializa con JSON.NET, ve que FullName no es un [DataMember]
y no lo serializa. ¿Hay una opción para pasar a JSON.NET para decirle que ignore el hecho de que una clase tiene el atributo [DataContract]
aplicado?
Nota: Usar JavaScriptSerializer en .NET funciona bien para la propiedad FullName, pero DateTimes está roto. Necesito que JSON.NET ignore el hecho de que esta clase tiene atributos DataContract / DataMember y simplemente hace la serialización estándar del campo público como lo haría si no estuvieran allí.
Como dijo Amry, puedes usar tu propio IContractResolver.
Lamentablemente, la solución provista por Amry no funcionó para mí, a continuación se muestra la solución que logré hacer funcionar:
public class AllPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
//property.HasMemberAttribute = true;
property.Ignored = false;
//property.ShouldSerialize = instance =>
//{
// return true;
//};
return property;
}
}
Se han comentado algunas líneas, estas no se requieren para que mi solución funcione, ¡pero nunca se sabe!
Esto tiene el mismo uso que la solución de Amry:
var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings {
ContractResolver = new AllPropertiesResolver()
});
¡Espero que esto ayude!
De acuerdo con la documentación de [DataMember]
atributos de [DataMember]
se ignoran si las propiedades también están anotadas con atributos específicos de Json.NET (como [JsonProperty]
). Consulte la documentación de Atributos de serialización para obtener más información:
Los atributos Json.NET toman presidencia sobre los atributos de serialización .NET estándar, por ejemplo, si ambos JsonPropertyAttribute y DataMemberAttribute están presentes en una propiedad y ambos personalizan el nombre, se usará el nombre de JsonPropertyAttribute.
La documentación solo cubre la propiedad de nombre, pero para mi experiencia, el atributo [JsonProperty]
también sombrea completamente la configuración realizada por el atributo [DataMember]
. Por lo tanto, si es factible para su caso, también agregue atributos Json.NET a las propiedades para las cuales se debe ignorar la anotación [DataMember].
Estaba teniendo un problema casi relacionado con lo que estaba teniendo, y logré encontrar una solución al revisar los códigos de Json.NET. Por lo tanto, puede que no sea la mejor solución, pero funciona para mí.
Para hacer esto, debe implementar su propio IContractResolver
. Una implementación demasiado simplificada de eso para incluir todos los parámetros e ignora todos los atributos (no solo DataContract
sino también otras reglas incorporadas de Json.NET, así que cualquier opción que establezca que originalmente debería afectar la selección de miembros ahora está siendo reemplazada por este código )
class AllPropertiesResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
return objectType.GetProperties()
.Where(p => p.GetIndexParameters().Length == 0)
.Cast<MemberInfo>()
.ToList();
}
}
Y aquí viene el ejemplo de uso del código:
var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings {
ContractResolver = new AllPropertiesResolver()
});
Si desea ignorar la presencia de DataContractAttribute
para todos los tipos sin tener que agregar atributos adicionales, entonces una resolución de contrato personalizada es la solución correcta. Sin embargo, a partir de Json.NET 9.0.1, la resolución de Amry ya no funciona. El solucionador de Doolali funciona pero tiene el efecto secundario adicional de serializar todas las propiedades públicas, incluidas las marcadas con [JsonIgnore]
. Si necesita una resolución de contrato que solo ignora la presencia de DataContractAttribute
pero se comporta como la resolución de contrato predeterminada, se puede utilizar lo siguiente:
public class IgnoreDataContractContractResolver : DefaultContractResolver
{
static MemberSerialization RemoveDataContractAttributeMemberSerialization(Type type, MemberSerialization memberSerialization)
{
if (memberSerialization == MemberSerialization.OptIn)
{
type = Nullable.GetUnderlyingType(type) ?? type;
// Json.NET interprets DataContractAttribute as inherited despite the fact it is marked with Inherited = false
// https://json.codeplex.com/discussions/357850
// https://.com/questions/8555089/datacontract-and-inheritance
// https://github.com/JamesNK/Newtonsoft.Json/issues/603
// Thus we need to manually climb the type hierarchy to see if one is present.
var dataContractAttribute = type.BaseTypesAndSelf().Select(t => t.GetCustomAttribute<DataContractAttribute>()).FirstOrDefault(a => a != null);
var jsonObjectAttribute = type.GetCustomAttribute<JsonObjectAttribute>();
if (dataContractAttribute != null && jsonObjectAttribute == null)
memberSerialization = MemberSerialization.OptOut;
}
return memberSerialization;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, RemoveDataContractAttributeMemberSerialization(type, memberSerialization));
return properties;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
contract.MemberSerialization = RemoveDataContractAttributeMemberSerialization(objectType, contract.MemberSerialization);
return contract;
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
Es posible que desee almacenar en caché el resolver del contrato para obtener el mejor rendimiento .
Simplemente use el atributo OptOut de Json.Net. Tendrá prioridad sobre DataContract.
[DataContract]
[JsonObject(MemberSerialization.OptOut)]