C# 运行时属性的Web API条件序列化

C# 运行时属性的Web API条件序列化,c#,rest,asp.net-web-api,C#,Rest,Asp.net Web Api,我正在考虑在ASP.Net中使用WebAPI构建API 我需要在运行时根据一些自定义逻辑有条件地从XML或JSON中排除属性,而不是编译时 我必须从响应中删除xml或json,仅仅包含带有null或空值的标记是没有好处的 我尝试过各种各样的方法,但似乎没有一种我能开始工作 我试过以下方法 授权处理程序 公共类响应的TAFilterHandler:DelegatingHandler { 受保护的重写System.Threading.Tasks.Task SendAsync(HttpRequestM

我正在考虑在ASP.Net中使用WebAPI构建API

我需要在
运行时根据一些自定义逻辑有条件地从XML或JSON中排除属性,而不是
编译时

我必须从响应中删除xml或json,仅仅包含带有null或空值的标记是没有好处的

我尝试过各种各样的方法,但似乎没有一种我能开始工作

我试过以下方法

授权处理程序

公共类响应的TAFilterHandler:DelegatingHandler
{
受保护的重写System.Threading.Tasks.Task SendAsync(HttpRequestMessage请求,CancellationToken CancellationToken)
{
return base.sendaync(请求、取消令牌)
.ContinueWith(任务=>
{
var response=task.Result;
//在此处操作内容
var content=response.content作为ObjectContent;
if(content!=null&&content.Value!=null)
{
}
//或者替换内容
//response.Content=newobjectcontent(typeof(object),newobject(),newmyformatter());
返回响应;
});
}
}
当然我可以在这里设置空属性,但是它们仍然出现在响应中

datacontractsrogate

公共类MySurrogate:IDataContractSurrogate
{
公共对象GetCustomDataToExport(类型clrType,类型dataContractType)
{
返回null;
}
公共对象GetCustomDataToExport(System.Reflection.MemberInfo MemberInfo,类型dataContractType)
{
返回null;
}
公共类型GetDataContractType(类型类型)
{
返回null;
}
公共对象GetDeserializedObject(对象对象,类型targetType)
{
返回null;
}
public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection customDataTypes)
{
}
公共对象GetObjectToSerialize(对象对象,类型targetType)
{
if(obj==null)返回null;
var type=obj.GetType();
type.GetProperties().ToList()类型
.ForEach(prop=>
{
尝试
{
var attr=prop.GetCustomAttributes(typeof(ConditionalDataMemberAttribute),false);
if(attr.Any())
{
var proptype=prop.PropertyType;
//将属性值设置为其默认值
prop.GetSetMethod().Invoke(obj,
新[]{proptype.IsValueType?Activator.CreateInstance(proptype):null};
}
}
捕获{}
});
返回obj;
}
公共类型GetReferencedTypeOnImport(字符串typeName、字符串typeNamespace、对象customData)
{
返回null;
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration,System.CodeDom.CodeCompileUnit compileUnit compileUnit)
{
返回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());
[DataContract]
public class MyClass
{
    [DataMember]
    public int Id { get; set; }

    [DataMember(EmitDefaultValue = false)]
    public string Name { get; set; }
}
然后,为了避免为Json和XML编写自定义转换器,您可以将Json转换为XML

XmlDocument doc = JsonConvert.DeserializeXmlNode(json);

只需像这样指定
EmitDefaultValue=false

string json = JsonConvert.SerializeObject(SomeObject, new MyJsonConverter());
[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;
}

好吧,我用我已经做过的一些东西,加上这里的一些建议,成功地做到了这一点,我也偶然发现了

首先,我们首先向管道中添加一个
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;
            });
    }
}
最后是
承包商版本

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(如果需要的话)实现的,对于我的测试项目,我使用一个querystring(Json=true)来指定格式是否应该是Json而不是xml。

您可以实现
ISerializable
,我已经尝试过了,并且在某种程度上它是有效的-但是你不能将它与
DataContract
属性一起使用,而且对于我拥有的嵌套集合和DTO的数量来说,维护并使输出看起来像我想要的一样是一件令人头疼的事情-我将再看一看:)这将从json响应json.NET中删除它也支持XML:酷,我不知道这一点。但是这种格式化会发生在json和xml mediatypeformatter中吗?您可以将json转换为xml,因此不必编写两个自定义转换器。有趣的是,现在我来看看:)这为我指明了正确的方向,但我并不热衷于编写所有逻辑来序列化自己-我设法只附加到每个属性的ShouldSerialize谓词。我喜欢这样,非常简单-但是有时我需要发出空值。用户不知道该元素是否因为权限而不存在,或者它是否真正为空。
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;
    }
}