I am looking at building an API using WebAPI in ASP.Net.
I have a requirement to conditionally exclude properties from the XML or JSON based on some custom logic at RunTime
and not Compile Time
.
I have to remove the xml or json from the response, it is no good just including the tags with a null or empty value.
I have tried various approaches, none of which I seem to be able to get to work.
I have tried the following
Delegating Handler from here
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;
});
}
}
Sure I can null properties out here, but they still appear in the response.
DataContractSurrogate similar to this
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;
}
}
Again, I can just null the properties out, I cannot remove the xml or json from the output.
I have an idea where I can dynamically compile a specific class with the required attributes then use the DataContractSurrogate to swap out the original instance with an instance of my new dynamic compiled class - but I don't like it.
Ive tried looking at DataContractSerializer
but it is sealed so I cant derive from it - i've also looked to decompile it and make some changes but again it uses internal classes such as DataContract
- I feel I need to hook into the serialization but I don't know how to ?
Just specify
EmitDefaultValue = false
like thisWith that, when
Name
is null, it will not shown up in XML/JSON.If you want to dynamically null out specific properties, you can provide a method like this.
You should use Json.NET and write your own converter
You can use your custom converter like this
Then, in order to avoid writing a custom Converter for both Json and XML, you can convert your Json to XML
Okay I managed to do this using a bit of what I had already done, plus some of the suggestions on here, I also stumbled upon this
First we start by adding a
DelegatingHandler
into the pipeline.And the class itself
Then we have a helper class method to get the new serialized string (this is not prod code ;p)
And finally the
ContractReoslver
this roots everything through Json and the converts to xml if required, for my test project I am using a querystring (json=true) to specify if the format should be json instead of xml.