类型名称在类上使用属性处理



我使用 TypeNameHandling 在 json 中序列化和反序列化派生类的列表。 它与属性和属性完美配合JsonProperty

public abstract class Animal
{
public bool CanFly { get; set;}
}
public class FlyingAnimal : Animal
{
public FlyingAnimal() { this.CanFly = true; }
}
public class SwimmingAnimal : Animal
{
public SwimmingAnimal() { this.CanFly = false; }
}
public class World
{
public World() { 
this.Animals = new List<Animal>(); 
this.Animals.Add(new FlyingAnimal());
this.Animals.Add(new SwimmingAnimal());
}
[JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
public List<Animal> Animals { get; set; }
}

现在,我需要一个返回派生类列表的 WebAPI:

[RoutePrefix("Animals")]
public class AnimalsController : ApiController
{
public List<Animal> Get()
{
List<Animal> animals = new List<Animal>(); 
animals.Add(new FlyingAnimal());
animals.Add(new SwimmingAnimal());
return animals;
}
}

是否有要放在基类上的属性以在序列化中包含类型?我尝试但没有成功:

[JsonObject(ItemTypeNameHandling = TypeNameHandling.Auto)]
public abstract class Animal

我知道我可以更改JsonSerializerSettings但我想要一个在基类上具有属性的解决方案

Json.NET 中没有开箱即用的功能,但您可以使用自定义合约解析器执行此操作:

[AttributeUsage(AttributeTargets.Class| AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
public class AddJsonTypenameAttribute : System.Attribute
{
}
public class AddJsonTypenameContractResolver : DefaultContractResolver
{
// As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
// http://www.newtonsoft.com/json/help/html/ContractResolver.htm
// http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
// "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
// See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
static AddJsonTypenameContractResolver instance;
// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
static AddJsonTypenameContractResolver() { instance = new AddJsonTypenameContractResolver(); }
public static AddJsonTypenameContractResolver Instance { get { return instance; } }
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
return base.CreateProperty(member, memberSerialization)
.ApplyAddTypenameAttribute();
}
protected override JsonArrayContract CreateArrayContract(Type objectType)
{
return base.CreateArrayContract(objectType)
.ApplyAddTypenameAttribute();
}
}
public static class ContractResolverExtensions
{
public static JsonProperty ApplyAddTypenameAttribute(this JsonProperty jsonProperty)
{
if (jsonProperty.TypeNameHandling == null)
{
if (jsonProperty.PropertyType.GetCustomAttribute<AddJsonTypenameAttribute>(false) != null)
{
jsonProperty.TypeNameHandling = TypeNameHandling.All;
}
}
return jsonProperty;
}
public static JsonArrayContract ApplyAddTypenameAttribute(this JsonArrayContract contract)
{
if (contract.ItemTypeNameHandling == null)
{
if (contract.CollectionItemType.GetCustomAttribute<AddJsonTypenameAttribute>(false) != null)
{
contract.ItemTypeNameHandling = TypeNameHandling.All;
}
}
return contract;
}
}

然后将其应用于接口或基类型,如下所示:

[AddJsonTypename]
public interface IAnimal
{
bool CanFly { get; }
}
[AddJsonTypename]
public abstract class Animal : IAnimal
{
public bool CanFly { get; set; }
}

请注意,该属性标有Inherited = false。 这意味着List<Animal>将自动插入类型信息,但List<FlyingAnimal>不会。 另请注意,这不会强制为根对象发出类型信息。如果您需要此内容,请参阅此处。

最后,要将协定解析程序与 Web API 配合使用,请参阅例如 Web API 2:如何在对象及其子对象上返回带有驼峰属性名称的 JSON。 请注意Newtonsoft文档中的这一警告:

当应用程序从外部源反序列化 JSON 时,应谨慎使用类型名称处理。使用非 None 的值反序列化时,应使用自定义序列化绑定程序验证传入类型。

有关为什么可能需要这样做的讨论,请参阅 Newtonsoft Json 中的 TypeNameHandling 警告、如何配置 Json.NET 以创建易受攻击的 Web API 和 Alvaro Muñoz & Oleksandr Mirosh 的黑帽论文 https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf。

也许不理想,但您可以尝试将其作为简单的解决方法:

[JsonArray(ItemTypeNameHandling = TypeNameHandling.Auto)]
public class AnimalList : List<Animal>
{ }

然后:

[RoutePrefix("Animals")]
public class AnimalsController : ApiController
{
public List<Animal> Get()
{
List<Animal> animals = new AnimalList(); 
animals.Add(new FlyingAnimal());
animals.Add(new SwimmingAnimal());
return animals;
}
}

相关内容

  • 没有找到相关文章

最新更新