Example Json:
{"Field1":"","Field2":null}.
In MVC, Field1 would be converted to null by default.
I tried the [DisplayFormat(ConvertEmptyStringToNull = true)] attribute, (which should be the default anyway) and it did not make a difference.
I'm using Web Api 2.1
Any ideas?
in C# an empty string is not the same as a null reference, and json.NET which is the underlying json implementation decided to avoid automatic conversions.
You can add the following custom converter to deal with that
public class EmptyToNullConverter : JsonConverter
{
private JsonSerializer _stringSerializer = new JsonSerializer();
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string value = _stringSerializer.Deserialize<string>(reader);
if (string.IsNullOrEmpty(value))
{
value = null;
}
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
_stringSerializer.Serialize(writer, value);
}
}
and to use it in your class decorate the properties you want to convert with
[JsonConverter(typeof(EmptyToNullConverter))]
public string FamilyName { get; set; }
You can add this converter to the config.Formatters.JsonFormatter.SerializerSettings.Converters and it will apply to all strings instead. Note that it required the private member _stringSerializer otherwise it will stackoverflow. The member is not required if you just decorate the string property directly.
in WebApiConfig.cs add the following line:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new EmptyToNullConverter());
Related
I wrote a custom JsonConverter called CompententsConverter and it works fine, however I'm curious if there is a way to make use of the alternate constructor which takes params object[] converterParameters and pass over my own custom parameters from the attribute accordingly.
Likewise, I am not sure how to actually retrieve the parameters inside the JsonConverter class definition or if it's even possible to do this with a JsonConverter attribute.
inside the model, attribute with theoretical params, where some_parameter_here is a placeholder for a constant expression:
[JsonProperty("components")]
[JsonConverter(typeof(ComponentsConverter), some_parameter_here)]
public List<ComponentModel> Components { get; set; }
ComponentsConverter custom JsonConverter:
public class ComponentsConverter : JsonConverter
{
public override bool CanConvert (Type t) => t == typeof(List<ComponentModel>);
public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// ... any way to access params object[] customParameters here?
}
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
// ...
}
}
Would be nice to be able to define some custom conversion behavior for specific model properties by using these extra params.
Json.NET basically just calls Activator.CreateInstance(ConverterType, ConverterParameters) [1] so the converter parameters are passed into the converter's constructor. You can remember them there and use them in ReadJson() and WriteJson() e.g. like so:
public class ComponentsConverter : JsonConverter
{
public string CustomString { get; init; }
public ComponentsConverter(string customString)
{
// Remember the converter parameters for use in WriteJson() and ReadJson()
this.CustomString = customString;
}
public override bool CanConvert (Type t) => t == typeof(List<ComponentModel>);
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
// Customize the serialized contents using the CustomString passed in to the constructor
writer.WriteValue(CustomString);
}
public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// ...
}
}
For a converter applied to the items of a collection, use JsonPropertyAttribute.ItemConverterType with JsonPropertyAttribute.ItemConverterParameters. E.g.:
[JsonProperty("components",
ItemConverterType = typeof(ComponentConverter),
ItemConverterParameters = new object [] { "custom_string_value" })]
And then:
public class ComponentConverter : JsonConverter
{
public string CustomString { get; init; }
public ComponentConverter(string customString)
{
// Remember the converter parameters for use in WriteJson() and ReadJson()
this.CustomString = customString;
}
public override bool CanConvert (Type t) => t == typeof(ComponentModel);
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
// Customize the serialized contents using the CustomString passed in to the constructor
writer.WriteValue(CustomString);
}
public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// ...
}
}
Demo fiddles here and here.
Footnotes:
[1]: I'm oversimplifying a little here. It actually uses code-generation techniques to generate and cache delegates on the fly that do the same thing as Activator.CreateInstance() but without the performance penalties of late-bound reflection. See e.g. ExpressionReflectionDelegateFactory.ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method).
I am using Xamarin forms and fetching a list of JSON objects, which are of multiple Types. E.g.
class BaseClass
{
public int Id;
}
class ClassA: BaseClass
{
public string Name;
}
class ClassB: BaseClass
{
public DateTime DateOfBirth;
}
and so on..
When I fetch such objects (ClassA, ClassB, ...), I use the following line to deserialize them into the BaseClass
var response = await RestClient.PostTaskAsync<List<BaseClass>>(request);
And latter try to cast them into ClassA or ClassB, only to get null for all the BaseClass objects.
I simply expect a List<BaseClass> containing ClassA, ClassB... objects.
What am I missing here? Is there a setting/configuration for the Serializer , where I could pass the specific classes to get the specifically serialized?
I was able to accomplish this with a custom JsonConverter
public class CustomConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return (objectType == typeof(BaseClass));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
JObject jo = JObject.Load(reader);
if (jo["identifyingField"].Value<string>() == "ExpectedValue")
return jo.ToObject<ConcreteClass>(serializer);
if (jo["identifyingField"].Value<string>() == "ExpentedValue2")
return jo.ToObject<ConcreteClass2>(serializer);
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
throw new NotImplementedException();
}
}
Luckily my objects had an identifying field in them which makes things easier...
If all of your concrete classes have unique fields to them, you can simply check jo["identifyingField"] != null
json.net has key method?
And to use your new CustomConverter...
var content = await httpResponse.Content.ReadAsStringAsync();
JsonConverter[] converters = { new CustomConverter() };
var result = JsonConvert.DeserializeObject<SomeObject>(content, new JsonSerializerSettings { Converters = converters});
You are telling it to deserialize the response to a list of BaseClass objects... that's exactly what it's returning to you. I'm not entirely sure how you would do a conditional deserialization. An approach I have used in the past is to have a PostTaskResponse object that defines all the possible properties you would have, then cast those to your expected result.
When trying to serialize a list of objects of polymorphic types (say a List<Shape> with Rectangle, Circle and Triangle objects), I need to prevent objects of a certain type from being serialized (say Circle).
What's the best way to achieve this? The JsonIgnore attribute seems to apply to object-properties and not entire types.
There is no built in way to ignore objects of a certain type, that are part of a list of objects of polymorphic types, from being serialized. What you can do is write a custom JsonConverter and decorate the Circle class with it. Example:
public class CircleJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
return;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
and example of the Circle class:
[JsonConverter(typeof(CircleJsonConverter))]
public class Circle : Shape
{
public uint R { get; set; }
}
You can implement the ReadJson method to be able to deserialize json string into instances of Circle class.
Drawback of this solution is that you won't be able to serialize into json string using JSON.NET any instance of the Circle class.
When I try to convert a json numeric field into C# string field, I get the following formatting issue:
Json format:
{"a":-7.0}
Corresponding C# class:
public class JsonReport
{
public string a {get;set;}
}
when I call the method:
var obj=JsonConvert.DeserializeObject<JsonReport>(strContent);
I expect the obj.a would be "-7.0", but it turn out to be "-7", which is not what I want , what should I set to fix the issue? Thanks.
If you have a decimal value like this, it is usually best to wait until the last possible moment--that is, just before you are about to display the value to the end user--before formatting it to a string. Waiting affords you the greatest flexibility in working with the value; you can do calculations with it, or decide to format it differently for different displays. So my recommendation would be to change the a property in your JsonReport class to be a double or decimal (depending on your needs), then format the value later, when you are ready to output it.
All that said, if you really want to format the decimal value to a string during deserialization, you can do this with a custom JsonConverter. Here is what it might look like:
class FloatToStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken value = JToken.Load(reader);
if (value.Type == JTokenType.Float)
{
// Adjust the formatting to your requirements
return value.ToObject<double>().ToString("0.0#######;-0.0#######");
}
return value.ToString();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, decorate the class properties you want formatted with a JsonConverter attribute like this:
public class JsonReport
{
[JsonConverter(typeof(FloatToStringConverter))]
public string a { get; set; }
}
Then you can deserialize as normal:
var obj = JsonConvert.DeserializeObject<JsonReport>(strContent);
Console.WriteLine(obj.a);
Output:
-7.0
pass your json object as string
{"a":"-7.0"}
Change your C# class
public class JsonReport
{
public string a {get;set;}
}
Suppose I have a object structure like this
Library 1 ---- 1+ Book 1 ---- 1+ Page
I want to serialize a json object of a book with an array of page objects.
Using JSON.net serializer, I can get this to serialize without getting a circular reference, but the JSON still includes all of the properties of the book in each page, which includes data about the library...which can have data on other books which is a ton of noise.
From the answer from this question - Serialize Entity Framework objects into JSON, I know that I can do generics, but is this really the only way? This just seems like a ton of extra work. Especially if for a Json result that is Book with and array of page objects in it.
I am using Entity Framework 4.3.1 and Json.net 4.0.30319...
You should look at the serialization attributes.
[JsonObject(MemberSerialization.OptIn)]
public class Page
{
[JsonProperty]
public string Text { get; set; }
// not serialized because mode is opt-in
public Book Book { get; set; }
}
Original answer
The aforementioned way should be prefered in most of the cases, but there are some where it is not enough.
There are two ways of doing it.
You can implement a JsonConverter, and override the WriteJson method to write only the properties you want.
class BookConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Book);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value.GetType() == typeof(T2))
{
JObject obj = new JObject();
Book b = value as Book;
obj["titre"] = b.Name;
obj["pages"] = b.Pages;
// This line can also be
// obj.WriteTo(writer, this);
// if you also need change the way pages are serialized.
obj.WriteTo(writer, null);
}
else
{
throw new NotImplementedException();
}
}
}
You can call it like that :
string result = JsonConvert.SerializeObject(
book,
new JsonSerializerSettings
{
Converters = new JsonConverter[] { new BookConverter() }
});
You can also create a JsonBook class, and serialize it.
class JsonBook{
public JsonBook(Book b){/*...*/}
public List<Pages> l;
public string title;
// No reference to Library.
}