运行时属性的 Web API 条件序列化
本文关键字:条件 序列化 API Web 属性 运行时 | 更新日期: 2023-09-27 18:33:51
我正在考虑在 ASP.Net 中使用WebAPI构建API。
我需要根据 RunTime
而不是 Compile Time
上的某些自定义逻辑有条件地从 XML 或 JSON 中排除属性。
我必须从响应中删除 xml 或 json,仅包含具有 null 或空值的标签是不好的。
我尝试了各种方法,但似乎没有一种方法能够开始工作。
我尝试了以下方法
从此处委派处理程序
public class ResponseDataFilterHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var response = task.Result;
//Manipulate content here
var content = response.Content as ObjectContent;
if (content != null && content.Value != null)
{
}
//Or replace the content
//response.Content = new ObjectContent(typeof(object), new object(), new MyFormatter());
return response;
});
}
}
当然,我可以在这里为空属性,但它们仍然出现在响应中。
数据合同代理与此类似
public class MySurrogate: IDataContractSurrogate
{
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
return null;
}
public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
return null;
}
public Type GetDataContractType(Type type)
{
return null;
}
public object GetDeserializedObject(object obj, Type targetType)
{
return null;
}
public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
{
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj == null) return null;
var type = obj.GetType();
type.GetProperties().ToList()
.ForEach(prop =>
{
try
{
var attr = prop.GetCustomAttributes(typeof(ConditionalDataMemberAttribute), false);
if (attr.Any())
{
var proptype = prop.PropertyType;
//Set the property value to its default value
prop.GetSetMethod().Invoke(obj,
new[] { proptype.IsValueType ? Activator.CreateInstance(proptype) : null });
}
}
catch { }
});
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
return null;
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
return null;
}
}
同样,我可以将属性清空,无法从输出中删除 xml 或 json。
我有一个想法,我可以动态编译具有所需属性的特定类,然后使用 DataContractSurrogate 将原始实例替换为我的新动态编译类的实例 - 但我不喜欢它。
我尝试查看DataContractSerializer
但它是密封的,所以我无法从中得出 - 我也希望反编译它并进行一些更改,但它再次使用内部类,例如 DataContract
- 我觉得我需要挂钩到序列化,但我不知道该怎么做?
使用 Json.NET 并编写自己的转换器
public class MyJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartArray();
// write your object here based on your custom logic
writer.WriteRawValue(value);
writer.WriteEndArray();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
您可以像这样使用自定义转换器
string json = JsonConvert.SerializeObject(SomeObject, new MyJsonConverter());
然后,为了避免为 Json 和 XML 编写自定义转换器,您可以将 Json 转换为 XML
XmlDocument doc = JsonConvert.DeserializeXmlNode(json);
我设法使用我已经做过的一些东西来做到这一点,加上这里的一些建议,我也偶然发现了这个
首先,我们首先向管道中添加一个DelegatingHandler
。
config.MessageHandlers.Add(new ResponseDataFilterHandler());
还有类本身
public class ResponseDataFilterHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var response = task.Result;
var content = response.Content as ObjectContent;
if (content != null && content.Value != null)
{
var isJson = response.RequestMessage.GetQueryNameValuePairs().Any(r => r.Key == "json" && r.Value == "true");
response.Content = new StringContent(Helper.GetResponseData(content.Value, isJson));
}
return response;
});
}
}
然后我们有一个辅助类方法来获取新的序列化字符串(这不是 prod 代码;p)
public static class Helper
{
public static string GetResponseData(object root,bool isJson)
{
string json = JsonConvert.SerializeObject(root, new JsonSerializerSettings { ContractResolver = new ShouldSerializeContractResolver()});
if (!isJson)
{
XmlDocument doc = JsonConvert.DeserializeXmlNode(json,"response");
json = doc.OuterXml;
}
return json;
}
}
最后是ContractReoslver
public class ShouldSerializeContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = (i) =>
{
//Your logic goes here
var r = !property.PropertyName.StartsWith("block-ref");
return r;
};
return property;
}
}
这通过 Json 植根所有内容,如果需要,可以转换为 XML,对于我的测试项目,我使用查询字符串 (json=true( 来指定格式是否应该是 json 而不是 xml。
只需像这样指定EmitDefaultValue = false
[DataContract]
public class MyClass
{
[DataMember]
public int Id { get; set; }
[DataMember(EmitDefaultValue = false)]
public string Name { get; set; }
}
这样,当Name
为空时,它将不会显示在 XML/JSON 中。
如果要动态清空特定属性,可以提供如下方法。
[OnSerializing]
void OnSerializing(StreamingContext context)
{
if(someConditionIsMet)
this.Name = null;
}