使用Json.net仅将接口属性序列化为JSON
使用像这样的简单类/接口
public interface IThing { string Name { get; set; } } public class Thing : IThing { public int Id { get; set; } public string Name { get; set; } }
如何只使用“Name”属性(仅底层接口的属性)获取JSON字符串?
实际上,当我做到这一点时:
var serialized = JsonConvert.SerializeObject((IThing)theObjToSerialize, Formatting.Indented); Console.WriteLine(serialized);
我得到完整的对象为JSON(Id + Name);
您可以使用条件序列化。 看看这个链接 。 基本上,您需要实现IContractResolver
接口,重载ShouldSerialize
方法并将解析器传递给Json Serializer的构造函数。
我用的方法,
public class InterfaceContractResolver : DefaultContractResolver { private readonly Type _InterfaceType; public InterfaceContractResolver (Type InterfaceType) { _InterfaceType = InterfaceType; } protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { //IList properties = base.CreateProperties(type, memberSerialization); IList 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);
受@ user3161686的启发,这是对InterfaceContractResolver
的一个小修改:
public class InterfaceContractResolver : DefaultContractResolver where TInterface : class { protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { IList properties = base.CreateProperties(typeof(TInterface), memberSerialization); return properties; } }
带有嵌套接口的改进版本+支持xsd.exe对象
这里还有另一种变化。 代码来自http://www.tomdupont.net/2015/09/how-to-only-serialize-interface.html,其中的其他答案有以下改进
- 处理层次结构,因此如果您在
Interface2[]
有一个Interface2[]
,那么它将被序列化。 -
我试图序列化一个WCF代理对象,结果JSON出现为
{}
。 原来所有属性都设置为Ignore=true
所以我不得不添加一个循环来将它们全部设置为不被忽略。public class InterfaceContractResolver : DefaultContractResolver { private readonly Type[] _interfaceTypes; private readonly ConcurrentDictionary
_typeToSerializeMap; public InterfaceContractResolver(params Type[] interfaceTypes) { _interfaceTypes = interfaceTypes; _typeToSerializeMap = new ConcurrentDictionary (); } protected override IList 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; } }
您可以添加[JsonIgnore]
注释以忽略属性。
[JsonIgnore]
的替代方法是[DataContract]
和[DataMember]
属性。 如果您的类被标记为[DataContract]
则序列化程序将仅处理使用[DataMember]
属性标记的属性( JsonIgnore
是“选择退出”模型,而DataContract
是“操作”)。
[DataContract] public class Thing : IThing { [DataMember] public int Id { get; set; } public string Name { get; set; } }
这两种方法的局限在于它们必须在类中实现,不能将它们添加到接口定义中。
除了@monrow给出的答案,你可以使用默认的[DataContract]和[DataMember]来看看这个
最后我得到它什么时候不起作用…如果你想进入另一个复杂的对象,它将无法正确序列化。
所以我制作的版本只会提取存储在特定程序集中的数据以及具有相同基本接口的类型。
所以它是.Net Core JsonContractResolver。
除了数据提取,它还解决了:
a)在向客户端发送数据之前进行camelCase转换
b)使用允许范围内的最顶层接口(通过汇编)c)修复字段顺序:首先列出大多数基类的字段,嵌套对象也符合此规则。
public class OutputJsonResolver : DefaultContractResolver { #region Static Members private static readonly object syncTargets = new object(); private static readonly Dictionary> Targets = new Dictionary>(); private static readonly Assembly CommonAssembly = typeof(ICommon).Assembly; #endregion #region Override Members protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { if (type.Assembly != OutputJsonResolver.CommonAssembly) return base.CreateProperties(type, memberSerialization); IList 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 CreateCustomProperties(Type type, MemberSerialization memberSerialization) { // Hierarchy IReadOnlyList types = this.GetTypes(type); // Head Type head = types.OrderByDescending(item => item.GetInterfaces().Length).FirstOrDefault(); // Sources IList sources = base.CreateProperties(head, memberSerialization); // Targets IList targets = new List (sources.Count); // Repository IReadOnlyDistribution repository = sources.ToDistribution(item => item.DeclaringType); foreach (Type current in types.Reverse()) { IReadOnlyPage page; if (repository.TryGetValue(current, out page) == true) targets.AddRange(page); } return targets; } private IReadOnlyList GetTypes(Type type) { List types = new List (); if (type.IsInterface == true) types.Add(type); types.AddRange(type.GetInterfaces()); return types; } #endregion }
我想分享一下我们在面对这项任务时最终会做些什么。 考虑到OP的界面和类……
public interface IThing { string Name { get; set; } } public class Thing : IThing { public int Id { get; set; } public string Name { get; set; } }
…我们创建了一个直接实现接口的类……
public class DirectThing : IThing { public string Name { get; set; } }
然后简单地序列化我们的Thing
实例,将其反序列化为DirectThing
,然后将其序列化为DirectThing
:
var thing = new Thing(); JsonConvert.SerializeObject( JsonConvert.DeserializeObject(JsonConvert.SerializeObject(thing)));
这种方法可以使用长接口inheritance链…您只需要在感兴趣的级别进行直接类(在此示例中为DirectThing
)。 无需担心reflection或属性。
从维护的角度来看,如果向IThing
添加成员,则DirectThing
类很容易维护,因为如果您还没有将它们放在DirectThing
,编译器会给出错误。 但是,如果您从IThing
删除成员X并将其放入Thing
,那么您必须记住将其从DirectThing
删除,否则X将在最终结果中。
从性能角度来看,此处有三个(反)序列化操作而不是一个,因此根据您的情况,您可能希望评估基于reflection器/属性的解决方案与此解决方案的性能差异。 在我的情况下,我只是在小范围内这样做,所以我不担心一些微/毫秒的潜在损失。
希望有人帮助!