基于查询参数的条件成员序列化?
Conditional member serialization based on query parameter?
我想根据查询参数与属性的匹配,控制模型中的哪些属性被序列化到我的 WebAPI2 JSON 响应。我这样做主要是为了减少 GET 上的带宽,而不会导致 ViewModel 类 的激增。例如:
GET /books/1?format=summary
public class Book
{
[SerializeFormat("summary")]
public int Id { get; set; }
[SerializeFormat("summary")]
public string Title { get; set; }
public string Contents { get; set; }
}
或
[SerializeFormat("summary","Id","Title")]
public class Book
{ ... }
为了自己做到这一点,我可以从实现 ISerializable
的自定义基础派生出我的所有模型 类。在 ISerializable.GetObjectData()
中,遍历检查属性的所有属性。不确定这个想法的表现。
不想重新发明这个解决方案,尽管它已经作为一个包存在。
一种可能性是引入可应用于属性和字段的自定义 attribute JsonConditionalIncludeAttribute
:
[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
public class JsonConditionalIncludeAttribute : System.Attribute
{
public JsonConditionalIncludeAttribute(string filterName)
{
this.FilterName = filterName;
}
public string FilterName { get; private set; }
}
接下来,subclass DefaultContractResolver
, override CreateProperty
和 return null 对于至少应用了一个 [JsonConditionalInclude]
的属性,其中 none 与 a提供给合同解析器的过滤器:
public class JsonConditionalIncludeContractResolver : DefaultContractResolver
{
public JsonConditionalIncludeContractResolver(string filterName)
{
this.FilterName = filterName;
}
public string FilterName { get; set; }
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
// Properties without JsonConditionalIncludeAttribute applied are serialized unconditionally.
// Properties with JsonConditionalIncludeAttribute are serialized only if one of the attributes
// has a matching filter name.
var attrs = property.AttributeProvider.GetAttributes(typeof(JsonConditionalIncludeAttribute), true);
if (attrs.Count > 0 && !attrs.Cast<JsonConditionalIncludeAttribute>().Any(a => a.FilterName == FilterName))
return null;
return property;
}
}
最后,在将您的 class 序列化为 JSON 时,将 JsonSerializerSettings.ContractResolver
设置为您的自定义合约解析器,从您的网络请求中初始化 FilterName
,例如:
public class TestClass
{
public string Property1 { get; set; }
[JsonConditionalInclude("summary")]
[JsonConditionalInclude("title")]
public string Property2 { get; set; }
[JsonConditionalInclude("summary")]
public string Property3 { get; set; }
[JsonConditionalInclude("title")]
[JsonConditionalInclude("citation")]
public string Property4 { get; set; }
[JsonConditionalInclude("citation")]
public string Field1;
public static void Test()
{
var test = new TestClass { Property1 = "a", Property2 = "b", Property3 = "c", Property4 = "d", Field1 = "e" };
Test(test, "summary"); // Prints "a", "b" and "c"
Test(test, "title"); // Prints "a", "b" and "d".
Test(test, "citation");// Prints "e", "a" and "d"
Test(test, null); // Prints "e", "a", "b", "c" and "d".
}
public static string Test(TestClass test, string webRequestFormat)
{
var settings = new JsonSerializerSettings { ContractResolver = new JsonConditionalIncludeContractResolver(webRequestFormat) };
var json = JsonConvert.SerializeObject(test, Formatting.Indented, settings);
Debug.WriteLine(json);
return json;
}
}
合同解析器将应用于所有正在序列化的 classes,而不仅仅是根 class,这看起来就是您想要的。
我想根据查询参数与属性的匹配,控制模型中的哪些属性被序列化到我的 WebAPI2 JSON 响应。我这样做主要是为了减少 GET 上的带宽,而不会导致 ViewModel 类 的激增。例如:
GET /books/1?format=summary
public class Book
{
[SerializeFormat("summary")]
public int Id { get; set; }
[SerializeFormat("summary")]
public string Title { get; set; }
public string Contents { get; set; }
}
或
[SerializeFormat("summary","Id","Title")]
public class Book
{ ... }
为了自己做到这一点,我可以从实现 ISerializable
的自定义基础派生出我的所有模型 类。在 ISerializable.GetObjectData()
中,遍历检查属性的所有属性。不确定这个想法的表现。
不想重新发明这个解决方案,尽管它已经作为一个包存在。
一种可能性是引入可应用于属性和字段的自定义 attribute JsonConditionalIncludeAttribute
:
[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
public class JsonConditionalIncludeAttribute : System.Attribute
{
public JsonConditionalIncludeAttribute(string filterName)
{
this.FilterName = filterName;
}
public string FilterName { get; private set; }
}
接下来,subclass DefaultContractResolver
, override CreateProperty
和 return null 对于至少应用了一个 [JsonConditionalInclude]
的属性,其中 none 与 a提供给合同解析器的过滤器:
public class JsonConditionalIncludeContractResolver : DefaultContractResolver
{
public JsonConditionalIncludeContractResolver(string filterName)
{
this.FilterName = filterName;
}
public string FilterName { get; set; }
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
// Properties without JsonConditionalIncludeAttribute applied are serialized unconditionally.
// Properties with JsonConditionalIncludeAttribute are serialized only if one of the attributes
// has a matching filter name.
var attrs = property.AttributeProvider.GetAttributes(typeof(JsonConditionalIncludeAttribute), true);
if (attrs.Count > 0 && !attrs.Cast<JsonConditionalIncludeAttribute>().Any(a => a.FilterName == FilterName))
return null;
return property;
}
}
最后,在将您的 class 序列化为 JSON 时,将 JsonSerializerSettings.ContractResolver
设置为您的自定义合约解析器,从您的网络请求中初始化 FilterName
,例如:
public class TestClass
{
public string Property1 { get; set; }
[JsonConditionalInclude("summary")]
[JsonConditionalInclude("title")]
public string Property2 { get; set; }
[JsonConditionalInclude("summary")]
public string Property3 { get; set; }
[JsonConditionalInclude("title")]
[JsonConditionalInclude("citation")]
public string Property4 { get; set; }
[JsonConditionalInclude("citation")]
public string Field1;
public static void Test()
{
var test = new TestClass { Property1 = "a", Property2 = "b", Property3 = "c", Property4 = "d", Field1 = "e" };
Test(test, "summary"); // Prints "a", "b" and "c"
Test(test, "title"); // Prints "a", "b" and "d".
Test(test, "citation");// Prints "e", "a" and "d"
Test(test, null); // Prints "e", "a", "b", "c" and "d".
}
public static string Test(TestClass test, string webRequestFormat)
{
var settings = new JsonSerializerSettings { ContractResolver = new JsonConditionalIncludeContractResolver(webRequestFormat) };
var json = JsonConvert.SerializeObject(test, Formatting.Indented, settings);
Debug.WriteLine(json);
return json;
}
}
合同解析器将应用于所有正在序列化的 classes,而不仅仅是根 class,这看起来就是您想要的。