Customizing JSON serialization in MVC action output - c#

Long ago, I set a coding standard for my app that all actions returning JSON would have their results put into a top-level wrapper object:
var result = {
success: false,
message: 'Something went wrong',
data: {} // or []
}
That has worked well, and provided me with good code standardization happiness.
Today, however, I realized that my server-side code assumes that it always gets to do the full serialization of what's getting returned. Now I would like to serialize one of these guys where the "data" payload is already a well-formed JSON string of its own.
This is the general pattern that had been working:
bool success = false;
string message = "Something went wrong";
object jsonData = "[{\"id\":\"0\",\"value\":\"1234\"}]"; // Broken
dynamic finalData = new { success = success, message = message, data = jsonData };
JsonResult output = new JsonResult
{
Data = finalData,
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
MaxJsonLength = int.MaxValue
};
return output;
Where it breaks is that the "data" element will be received as a string when it gets to the browser, and not as the proper JSON object (or array in the example above) it should be.
Is there some way I can decorate a property with an attribute that says "serialize as raw", or am I in the realm of writing a custom JSON serializer to make this work?

You're serializing it twice (jsonData + output). You can't do that and expect to only deserialize it once (output).
You could set the "data" object in your dynamic to be the real c# object, that would work. Or you can re-name your property to "jsonData":
dynamic finalData = new { success = success, message = message, jsonData = jsonData };
...so it reflects what you're really doing :).

I would think that you just need to serialize the string being returned from the SQL table into an object, using a JSON serializer, like NewtonSoft.
bool success = false;
string message = "Something went wrong";
string rawData = "[{\"id\":\"0\",\"value\":\"1234\"}]"; // Broken
object jsonData = JsonConvert.DeserializeObject<dynamic>(rawData);
dynamic finalData = new { success = success, message = message, data = jsonData };
JsonResult output = new JsonResult
{
Data = finalData,
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
MaxJsonLength = int.MaxValue
};
return output;

Here's what I ended up with....
// Wrap "String" in a container class
public class JsonStringWrapper
{
// Hey Microsoft - This is where it would be nice if "String" wasn't marked "sealed"
public string theString { get; set; }
public JsonStringWrapper() { }
public JsonStringWrapper(string stringToWrap) { theString = stringToWrap; }
}
// Custom JsonConverter that will just dump the raw string into
// the serialization process. Loosely based on:
// http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm
public class JsonStringWrapperConverter : JsonConverter
{
private readonly Type _type = typeof(JsonStringWrapper);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
string rawValue = ((JsonStringWrapper)value).theString;
writer.WriteRawValue((rawValue == null) ? "null" : rawValue);
}
}
public override bool CanWrite
{
get { return true; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return _type == objectType;
}
}
// Custom JsonResult that will use the converter above, largely based on:
// http://stackoverflow.com/questions/17244774/proper-json-serialization-in-mvc-4
public class PreSerializedJsonResult : JsonResult
{
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new JsonStringWrapperConverter() }
};
public override void ExecuteResult(ControllerContext context)
{
if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("GET request not allowed");
}
var response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(this.ContentType) ? this.ContentType : "application/json";
if (this.ContentEncoding != null)
{
response.ContentEncoding = this.ContentEncoding;
}
if (this.Data == null)
{
return;
}
response.Write(JsonConvert.SerializeObject(this.Data, Settings));
}
}
// My base controller method that overrides Json()...
protected JsonResult Json(string message, object data)
{
PreSerializedJsonResult output = new PreSerializedJsonResult();
object finalData = (data is string && (new char[] { '[', '{' }.Contains(((string)data).First())))
? new JsonStringWrapper(data as string)
: data;
output.Data = new
{
success = string.IsNullOrEmpty(message),
message = message,
data = finalData
};
output.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
output.MaxJsonLength = int.MaxValue;
return output;
}
// Aaaand finally, here's how it might get called from an Action method:
...
return Json("This was a failure", null);
...
return Json(null, yourJsonStringVariableHere);
With this, I'm not doing any Json parsing on the server. My string comes out of the database and goes straight to the client without MVC touching it.
EDIT: Updated version now also supports serializing objects that have individual properties somewhere in their hierarchy that are of type JsonStringWrapper. This is useful in my scenario to support a "hybrid" model. If object A has a property B that is one of my pre-baked JSON strings, the code above will properly handle that.

You could accomplish this by forming the JSON package yourself using the JsonWriter class from Newtonsoft. It would look something like this:
using(var textWriter = new StringWriter())
using(var jsonWriter = new JsonTextWriter(textWriter))
{
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("success");
jsonWriter.WriteValue(success);
jsonWriter.WritePropertyName("message");
jsonWriter.WriteValue(message);
jsonWriter.WritePropertyName("data");
jsonWriter.WriteRaw(jsonData);
jsonWriter.WriteEndObject();
var result = new ContentResult();
result.Content = textWriter.ToString();
result.ContentType = "application/json";
return result;
}

Related

IgnoreNullValue not working when serializing an Anonymous Object

The Problem
I am creating an anonymous object in a method and assigning it properties from a class. Some of the properties can sometimes be empty (strings) and I eventually need to serialize the anonymous object to a JSON string.
When it gets serialized as JSON, I want to get rid of nulls or empty strings. I actually want to do this on a property by property basis (not generically ignoring all nulls) but it doesn't seem to be working at all!
The Code
public class Refrigerator {
public string Brand { get; set; }
public bool HasFood { get; set; }
public void Serialize() {
//ANONYMOUS OBJECT HERE
var fridge = new {
Brand = this.Brand; //THIS VALUE IS SOMETIMES AN EMPTY STRING!!
HasFood = this.HasFood;
}
var value = Newtonsoft.Json.JsonConvert.SerializeObject(fridge);
//value contains Brand as an empty string
}
}
What I've Tried
var fridge = new {
Brand = this.Brand;
HasFood = this.HasFood;
//JsonSerializer should check this function I thought?
ShouldSerializeBrand = new Func<bool>() {
if(String.IsNullOrEmpty(this.Brand){
return false;
}
return true;
}
}
//This should also work, but doesn't.......
var value = Newtonsoft.Json.JsonConvert.SerializeObject(fridge, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
});
//JSON STILL contains Brand as an empty string!!! Its still there!
Any ideas why its not working?
One of the way for this could be that, you can use ExpandoObject for this like :
dynamic fridge = new ExpandoObject();
if(!String.IsNullOrEmpty(this.Brand))
{
fridge.Brand = this.Brand;
}
fridge.HasFood = this.HasFood;
This way the anonymous object will not have Brand property created unless it has a value. Now if the Brand is null of empty string, the property won't be created on the anonymous object we have created.
See this DEMO FIDDLE as example.
An empty string i.e. the literal "" is neither a null value nor the default for a string type. As such, both options from your JsonSerializerSettings will fail to do what you want. You can solve the problem by doing what you already tried, or by creating a custom JSONConverter to add in your specific serialization logic.
Sample code based off the Newtonsoft samples:
public class FridgeJsonConverter : JsonConverter
{
private readonly Type[] _types;
public FridgeJsonConverter(params Type[] types)
{
_types = types;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
var brandProp = o.Properties().First(x => x.Name == "Brand");
if(brandProp.Value.ToString() == string.Empty)
o.Remove(brandProp.Name);
o.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return _types.Any(t => t == objectType);
}
}
Then, in your calling code, do this:
string json = JsonConvert.SerializeObject(fridge, Formatting.Indented, new FridgeJsonConverter(typeof(Fridge)));

Deserializing a list of dynamic properties

My application is consuming an API, and I'm trying to deserialize data of images coming back. The data is formatted like:
{
"images":{
"totalCount":4,
"0":{
"url":"file1.jpg"
},
"1":{
"url":"file2.jpg"
},
"2":{
"url":"file3.jpg"
},
"3":{
"url":"file4.jpg"
}
}
}
I have these model classes:
public class MyViewModel
{
[JsonProperty("images")]
public ImagesViewModel Images { get; set; }
}
public class ImagesViewModel
{
[JsonProperty("totalCount")]
public int TotalCount { get; set; }
public Dictionary<string, ImageViewModel> ListImages { get; set; }
}
public class ImageViewModel
{
[JsonProperty("url")]
public string Url { get; set; }
}
The collection of images isn't really a collection, for some reason it's just a new property for each image. I'm trying to deserialize my object like:
... // create HttpClient object, add headers and such
System.Net.Http.HttpResponseMessage response = await
client.GetAsync(endpointUrl);
var jsonString = response.Content.ReadAsStringAsync();
MyViewModel model =
JsonConvert.DeserializeObject<MyViewModel>(jsonString.Result);
I get back the totalCount property just fine, but the collection of images is coming back null.
Is there a way for me to change my view models so that I can deserialize the json correctly?
Given the formatting of the JSON you will have to go the long route and try to deserialize it using JObjects
//using Newtonsoft.Json.Linq
var jObject = JObject.Parse(jsonString);
var images = jObject.Property("images").Value<JObject>(); ;
var viewModel = new MyViewModel {
Images = new ImagesViewModel {
TotalCount = images.Property("totalCount").Value<int>(),
ListImages = images.Properties().Skip(1).ToDictionary(p => p.Name, p => p.Value<ImageViewModel>())
}
};
Going a step further and using a JsonConverter for converting the payload itself actually works as well given that we know now how to convert it.
public class MyViewModelConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return objectType == typeof(MyViewModel);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
var jObject = JObject.Load(reader);//<-- Note the use of Load() instead of Parse()
var images = jObject.Property("images").Value<JObject>(); ;
var model = new MyViewModel {
Images = new ImagesViewModel {
TotalCount = images.Property("totalCount").Value<int>(),
ListImages = images.Properties().Skip(1).ToDictionary(p => p.Name, p => p.Value<ImageViewModel>())
}
};
return model;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
throw new NotImplementedException();
}
}
and decorating the class itself
[JsonConverter(typeof(MyViewModelConverter))]
public class MyViewModel {
[JsonProperty("images")]
public ImagesViewModel Images { get; set; }
}
Deserialization is now as you intended to do before
var jsonString = await response.Content.ReadAsStringAsync();
MyViewModel model = JsonConvert.DeserializeObject<MyViewModel>(jsonString);
.NET Abhors dynamic types. They fly in the face of solid type checking at compile time. That being said, there is support for it:
As the example data is basically just a array of images, any collection could deal with this input.
If you can not even define the types umanbigiously (you might have a array of images and one of strings), the only way is ExpandoObject. It is designed specifically to deal with such cases. It is basically a List[string, object] with some Syntax Sugar, but it also does includes functions like Property Change Notifications.
Sounds like a job for a custom converter!
A custom converter will let you supply your own logic for deserializing specific types. Newtonsoft uses the target class to figure out with type if expects to find in the json and call the appropriate converter.
class ImagesViewModelConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ImagesViewModel);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
assertToken(JsonToken.StartObject);
var obj = new ImagesViewModel()
{
ListImages = new Dictionary<string, ImageViewModel>()
};
while (reader.Read() && reader.TokenType != JsonToken.EndObject)
{
assertToken(JsonToken.PropertyName);
var propName = (string)reader.Value;
if (propName.Equals(nameof(ImagesViewModel.TotalCount), StringComparison.InvariantCultureIgnoreCase))
{
reader.Read();
assertToken(JsonToken.Integer);
obj.TotalCount = (int)((Int64)reader.Value);
continue;
}
reader.Read();
var image = serializer.Deserialize<ImageViewModel>(reader); // you can still use normal json deseralization inside a converter
obj.ListImages.Add(propName, image);
}
return obj;
void assertToken(JsonToken token)
{
if (reader.TokenType != token)
throw new Exception(); // might wanna add detailed errors
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException(); // implement if needed
}
}
And then:
var settings = new JsonSerializerSettings()
{
Converters = new[] { new ImagesViewModelConverter() }
};
var obj = JsonConvert.DeserializeObject<MyViewModel>(jsonString, settings);
});
You can even change classes to be easier to handle, given that they no longer need to match the json exactly. You can for example replace the dict with an array and have the converter fill it in order.

JsonReader only reads JValue of type string

In my scenario two servers talk via a secured WebSocket connection. Sent messages are well defined json objects. Since I'm dealing with WebSockets I use streams to deserialize the received json into objects.
However, in my custom converter the received json always gets deserialized into a JValue of type string. I spent the better part of the day trying to fix this and in the end had to convert the JValue to a string just to parse it back into a JObject, and -- as far as I can tell -- totally negating the advantage of a stream based approach.
Is this the intended behaviour? If not, how can I fix this?
This is the code in question. The custom serializes is registered in the SerializerSettings object.
public Task ReceiveAsync(WebSocket socket, WebSocketReceiveResult result, byte[] buffer)
{
SocketMessage message;
try
{
var ser = JsonSerializer.Create(this.SerializerSettings);
using (var sReader = new StreamReader(new MemoryStream(buffer)))
{
using (var jReader = new JsonTextReader(sReader))
{
message = ser.Deserialize<SocketMessage>(jReader);
}
}
}
catch (JsonReaderException jex)
{
this.Logger.LogError(1, jex, "Received json was not in the correct format.");
return Task.FromException(jex);
}
//... Remainder removed for brevity
}
public class SocketMessage
{
public SocketMessage()
{
this.Headers = new Dictionary<string, string>(5);
}
public IDictionary<string, string> Headers { get; set; }
public JToken Content { get; set; }
}
public sealed class SocketMessageJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SocketMessage);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var jString = (string)JToken.ReadFrom(reader);
var jVal = JObject.Parse(jString);
var headers = jVal.Children<JProperty>()
.Where(p => p.Name.Equals("headers", StringComparison.OrdinalIgnoreCase))
.Values()
.Children()
.Select(c => c as JProperty)
.Where(p => p != null)
.ToDictionary(p => p.Name, p => (string)p.Value);
return new SocketMessage
{
Headers = headers,
Content = jVal["content"],
};
}
}
Here is a sample Json that was received (taken with Encoding.UTF8.GetString(buffer).TrimEnd('\0');)
The Content property is an arbitrary property that is, based on certain header fields, going to be deserialized elsewhere in the code. Therefore I only store it as JObject in the SocketMessage.
#"{""headers"":{""action"":""opened""},""content"":""address"":""localhost"",""map_servers"":[]}}"

mvc6 model binder not picking untagged JSON

I'm trying to receive a JSON in a MVC6 controller. I see my function being triggered when debugging, but the param is always null.
The JSON is like this (it comes from an external software, so I can't change it. Also notice the ContentType is "application/w-xxx-form-urlencoded"):
["String1","String2",["StringInAList1","StringInAList2",...]]
My code looks like this:
[HttpPost]
public void ReceiveJson([FromBody] MyJson json)
{
//stuff
}
public class MyJson
{
public string string1 { get; set; }
public string string2 { get; set; }
public List<string> Data { get; set; }
}
Any ideas of what's wrong here?
Firstly, that JSON is an array, not an object. A JSON object would look like this
{
"MessageName": "String1",
"UserName": "String2",
"AdditionalData": ["StringInAList1","StringInAList2",...]
}
Since non-enumerable c# classes are mapped to JSON objects, asp.net isn't going to be able to deserialize that JSON to your class by default.
Secondly, going by the documentation here, AdditionalData can be any array of strings or nested arrays of the same type, not just a simple array of strings, for instance:
["GameEnded","Risterral",[["Risterral", "Won"],["PlayerName2", "Lost"]]]
A simple List<string> Data won't be able to represent this.
Since asp.net uses Json.NET by default for JSON serialization, the easiest way to represent such a polymorphic array is as a JArray, which can represent any JSON array in memory. You will also need a custom JsonConverter for the top level object. Thus:
public enum HexRequestType
{
Unknown,
Login,
Logout,
SaveDeck,
DraftPack,
DraftCardPicked,
GameStarted,
GameEnded,
Collection,
}
[JsonConverter(typeof(HexRequestConverter))]
public class HexRequest
{
[JsonIgnore]
public HexRequestType RequestType
{
get
{
if (string.IsNullOrEmpty(MessageName))
return HexRequestType.Unknown;
if (MessageName.Equals("DaraftCardPicked", StringComparison.OrdinalIgnoreCase))
return HexRequestType.DraftCardPicked;
try
{
return (HexRequestType)Enum.Parse(typeof(HexRequestType), MessageName, true);
}
catch (Exception)
{
Debug.WriteLine("Unknown request " + MessageName);
return HexRequestType.Unknown;
}
}
}
public string MessageName { get; set; }
public string UserName { get; set; }
public JArray AdditionalData { get; set; }
}
public class HexRequestConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(HexRequest);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var array = JToken.Load(reader) as JArray;
if (array == null)
return existingValue;
var request = existingValue as HexRequest ?? new HexRequest();
request.MessageName = (array.Count > 0 ? (string)array[0] : null);
request.UserName = (array.Count > 1 ? (string)array[1] : null);
request.AdditionalData = (JArray)(array.Count > 2 ? array[2] : null) ?? new JArray();
if (array.Count > 3)
{
Debug.WriteLine("array too large");
throw new InvalidOperationException();
}
return request;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var request = (HexRequest)value;
var list = new List<object> { request.MessageName, request.UserName, request.AdditionalData };
serializer.Serialize(writer, list);
}
}
Having done that, you can use Linq and Linq-to-JSON to manually extract the expected data, for instance:
switch (request.RequestType)
{
case HexRequestType.GameEnded:
{
// ["GameEnded","Risterral",[["Risterral", "Won"],["PlayerName2", "Lost"]]]
var results = request.AdditionalData.OfType<JArray>().Select(a => new { Player = (string)a[0], Result = (string)a[1] }).ToList();
Debug.WriteLine(JsonConvert.SerializeObject(results));
// Outputs [{"Player":"Risterral","Result":"Won"},{"Player":"PlayerName2","Result":"Lost"}]
}
break;
// Other cases as needed
default:
break;
}

Web API Conditional Serialization of properties at runtime

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 ?
You should use Json.NET and write your own converter
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;
}
}
You can use your custom converter like this
string json = JsonConvert.SerializeObject(SomeObject, new MyJsonConverter());
Then, in order to avoid writing a custom Converter for both Json and XML, you can convert your Json to XML
XmlDocument doc = JsonConvert.DeserializeXmlNode(json);
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.
config.MessageHandlers.Add(new ResponseDataFilterHandler());
And the class itself
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;
});
}
}
Then we have a helper class method to get the new serialized string (this is not prod code ;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;
}
}
And finally the 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;
}
}
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.
Just specify EmitDefaultValue = false like this
[DataContract]
public class MyClass
{
[DataMember]
public int Id { get; set; }
[DataMember(EmitDefaultValue = false)]
public string Name { get; set; }
}
With 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.
[OnSerializing]
void OnSerializing(StreamingContext context)
{
if(someConditionIsMet)
this.Name = null;
}

Categories