我需要Web API返回以json格式序列化的Rule
个实例的列表。
[HttpGet]
[SwaggerOperation(nameof(GetRules))]
[SwaggerResponse(StatusCodes.Status200OK, typeof(List<Rule>), "Rules")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> GetRules()
{
List<Rule> rules = /* retrieve rule from some storage */;
return Ok(rules);
}
目前,有两种规则,每种规则在Rule类中共享的规则之上都有特定的属性。一个规则称为RuleWithExpiration
,另一个规则称为RuleWithGracePeriod
。
[JsonObject(MemberSerialization.OptIn)]
public class Rule
{
[JsonProperty("id")]
public Guid Id { get; }
[JsonProperty("name")]
public string Name { get; }
[JsonConstructor]
public Rule(Guid id, string name)
{
Id = id;
Name = name;
}
}
[JsonObject(MemberSerialization.OptIn)]
public class RuleWithExpiration : Rule
{
[JsonProperty("someInfo")]
public string SomeInfo { get; }
[JsonProperty("expiration")]
DateTime Expiration { get; }
[JsonConstructor]
public RuleWithExpiration(Guid id, string name, string someInfo, DateTime expiration) : base(id, name)
{
SomeInfo = someInfo;
Expiration = expiration;
}
}
[JsonObject(MemberSerialization.OptIn)]
public class RuleWithGracePeriod : Rule
{
[JsonProperty("gracePeriod")]
public TimeSpan GracePeriod { get; }
[JsonConstructor]
public RuleWithGracePeriod(Guid id, string name, TimeSpan gracePeriod) : base(id, name)
{
GracePeriod = gracePeriod;
}
}
我遇到的问题是,当我尝试反序列化该类层次结构时,会遇到问题。反序列化之后,由于没有要求序列化程序将类型信息包含为it is considered a security issue,所以我得到了Rule
实例列表。
void Main()
{
List<Rule> rules = new List<Rule>
{
new RuleWithExpiration(Guid.NewGuid(), "Rule with expiration", "Wat?", DateTime.UtcNow.AddHours(1d)),
new RuleWithGracePeriod(Guid.NewGuid(), "Rule with grace period", TimeSpan.FromHours(1d)),
};
var serializedRule = JsonConvert.SerializeObject(rules);
serializedRule.Dump();
List<Rule> deserializedRule = JsonConvert.DeserializeObject<List<Rule>>(serializedRule);
deserializedRule.Dump();
}
这是序列化的字符串:
[{"someInfo":"Wat?","expiration":"2018-07-26T13:32:06.2287669Z","id":"29fa0603-c103-4a95-b627-0097619a7645","name":"Rule with expiration"},{"gracePeriod":"01:00:00","id":"bd8777bb-c6b3-4172-916a-546775062eb1","name":"Rule with grace period"}]
这是我对其反序列化后得到的Rule
个实例的列表(如LINQPad中所示):
问题
是否可以在此上下文中保留此继承树,还是必须以某种方式重新排列这些类?如果是这样,怎么办呢?
解决方案
我还没有找到感觉良好的解决方案。
例如,我可以吃一些
RuleAggregate
类就像这样,但是每次我引入一种新规则时,我都必须编辑该类并处理影响:
[JsonObject(MemberSerialization.OptIn)]
public class RuleAggregate
{
[JsonProperty("expirations")]
public List<RuleWithExpiration> Expirations {get;}
[JsonProperty("gracePeriods")]
public List<RuleWithGracePeriod> GracePeriods {get;}
[JsonConstructor]
public RuleAggregate(List<RuleWithExpiration> expirations, List<RuleWithGracePeriod> gracePeriods)
{
Expirations = expirations;
GracePeriods = gracePeriods;
}
}
如果我想保留继承树,那么我在权衡较小的情况下找到的解决方案就是依靠良好的XML序列化。
答案 0 :(得分:1)
好的,正确的TypeNameHandling.All
使其容易受到攻击。那这种方法呢?
void Main()
{
Stockholder stockholder = new Stockholder
{
FullName = "Steve Stockholder",
Businesses = new List<Business>
{
new Hotel
{
Name = "Hudson Hotel",
Stars = 4
}
}
};
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
SerializationBinder = new KnownTypesBinder { KnownTypes = new List<Type> { typeof(Stockholder), typeof(Hotel) }}
};
string ok;
/*
ok = JsonConvert.SerializeObject(stockholder, Newtonsoft.Json.Formatting.Indented, settings);
Console.WriteLine(ok);*/
ok = @"{
""$type"": ""Stockholder"",
""FullName"": ""Steve Stockholder"",
""Businesses"": [
{
""$type"": ""Hotel"",
""Stars"": 4,
""Name"": ""Hudson Hotel""
}
]
}";
JsonConvert.DeserializeObject<Stockholder>(ok, settings).Dump();
var vector = @"{
""$type"": ""Stockholder"",
""FullName"": ""Steve Stockholder"",
""Businesses"": [
{
""$type"": ""System.IO.FileInfo, System.IO.FileSystem"",
""fileName"": ""d:\rce-test.txt"",
""IsReadOnly"": true
}
]
}";
JsonConvert.DeserializeObject<Stockholder>(vector, settings).Dump(); // will fail
}
public class KnownTypesBinder : ISerializationBinder
{
public IList<Type> KnownTypes { get; set; }
public Type BindToType(string assemblyName, string typeName)
{
return KnownTypes.SingleOrDefault(t => t.Name == typeName);
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
}
public abstract class Business
{
public string Name { get; set; }
}
public class Hotel: Business
{
public int Stars { get; set; }
}
public class Stockholder
{
public string FullName { get; set; }
public IList<Business> Businesses { get; set; }
}