c# - Ignore empty/null value and guid empty in WebApiConfig - c#

I have a controller that returns a json in a function GET. I want to ignore\delete the empty, null value and the guide empty of my json, that I sent to a third part.
In the WebApiConfig I use:
config.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
And it works for the null value, but not to the empty\guid.empty value.
So I try to add config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new EmptyToNullConverter()); and the class EmptyToNullConverter. To convert the empty value in null and then removed by the other command but it doesn't work because it doesn't convert the values.
the class EmptyToNullConverter is:
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 == Guid.Empty.ToString())
{
value = null;
}
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
_stringSerializer.Serialize(writer, value);
}
}
How can I do?
Thanks a lot

Even when you are serializing if the object type is Guid. It still can't be null, trying to override at a serialization level could create confusion anyway as if the receiver is expecting a Guid in a response model. Even if you send nothing in a JSON response, their code may still initialise an empty Guid.
Is there any reason you couldn't use a nullable Guid? Given you are new to stack overflow i'm going to perhaps rudely assume your new to coding and so you might not be familiar with nullable value types.
Value types such as int, float, GUID, DateTime etc... will always have a default value, however you can force them to be nullable by declaring them with a question mark suffixed.
e.g.
int x // == 0;
int? x // == null;
Guid g // == 00000000-0000-0000-0000-000000000000;
Guid? g // == null;
A good general rule is that if an value type can be essentially null, then make it nullable. Then you won't need any fancy conversions.

Related

Handling invalid inputs when deserializing JSON to decimal values

I have a JSON file with an array of objects, each containing a string value, grade, that I'd like to parse to decimal.
The string value contains a valid decimal about 99% percent of the time, but in that 1%, I'm getting values such as "grade":"<1" which is obviously not a valid decimal. The grade property is about 1 of 100 properties that can sometimes be set to "<1".
This of course throws the following error:
Newtonsoft.Json.JsonReaderException: 'Could not convert string to
decimal'
Here is how I am currently deserializing my JSON:
public static Product FromJson(string json) => JsonConvert.DeserializeObject<Product>(json, Converter.Settings);
Is there anything I can do to handle cases where I'm getting those pesky "<1" values? Hopefully something that does the following: if attempting to deserialize a value to decimal, and if the value cannot be parsed to decimal, default to zero.
Any ideas if this is possible? I obviously don't want to have to update my table columns to switch all values from decimal to varchar, because that just sucks and is going to require decimal <-> varchar conversions every time someone wants to query my data.
You can solve this problem by making a custom JsonConverter to handle the decimals:
class TolerantDecimalConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(decimal);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Float || reader.TokenType == JsonToken.Integer)
{
return Convert.ToDecimal(reader.Value);
}
if (reader.TokenType == JsonToken.String && decimal.TryParse((string)reader.Value, out decimal d))
{
return d;
}
return 0.0m;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, just add an instance to the Converters collection in the JsonSerializerSettings that you are passing to JsonConvert.DeserializeObject<T>.
Settings.Converters.Add(new TolerantDecimalConverter());
Note: since you are using decimals, you should probably also set FloatParseHandling to Decimal if you are not already; the default is Double.
Settings.FloatParseHandling = FloatParseHandling.Decimal;
Working demo here: https://dotnetfiddle.net/I4n00o

How to deserialize Booleans from JSON case-insensitive?

I'm having some difficulties in deserializing a Boolean from a json. I need that the value can be case insensitive (faLSe, tRUE, etc) and if it's an invalid value (ex: qwerty) I'll return a null. I've created a custom converter:
public class NullableBooleanJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// Handle only boolean types.
return objectType == typeof(bool?);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string value = reader?.Value?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
if (bool.TryParse(value, out bool deserializedValue))
{
return deserializedValue;
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
}
And I'm using the code like this:
JsonSerializerSettings setting = new JsonSerializerSettings
{ Converters = new List<JsonConverter> { this.converter } };
JsonConvert.DeserializeObject<bool?>("false", setting).Should().BeFalse();
and this works, but if I have on the last line:
JsonConvert.DeserializeObject<bool?>("faLSE", setting).Should().BeFalse();
The test fails with the message:
TestName threw exception:
Newtonsoft.Json.JsonReaderException: Error parsing boolean value. Path '', line 1, position 2
What I'm doing wrong?
If you look at the source code of DeserializeObject it uses the JsonTextReader class to parse the Json. If you look at the source code of JsonTextReader it parses only "true" (if first char is a lowercase t) or "false" (if first char a lowercase f) strings (row 1720), throwing an exception in any other case.
The simplest way to realize what you need is to download the full Newtonsoft.Json source code, add a String.ToLower to ParseTrue and ParseFalse methods in JsonTextReader.cs, recompile and use the new dll in your project.

JsonConvert.DeserializeObject throws an exception when deserializing JSON with a hex value into an sbyte property

I have a machine-generated class MyData which has sbyte members. The actual class is long and not very readable, but here is a fragment of it:
class MyData
{
private sbyte _MuxControl;
public sbyte MuxControl
{
get { return _MuxControl; }
set { __isset.MuxControl = true; this._MuxControl = value; }
}
}
The corresponding simplified JSON looks like this:
[
{
"MuxControl": 0xAA
}
]
I am attempting to deserialize like this:
var deserialized = JsonConvert.DeserializeObject<List<MyData>>(JsonStr);
Some values exceed sbyte range, for example 0xAA. As a result, exceptions are thrown. When I change the value to 0x1, for example, it works.
I can not touch the code of MyData. It's machine-generated. Is there a conversion setting, override or some other way to get these values to deserialize properly into sbyte?
You are getting the exception because the conversion methods Json.Net is using under the covers for sbyte are range checked, but what you really need here is an unchecked conversion (or more precisely, a bigger range). You can handle that with a custom JsonConverter like so:
public class SByteConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(sbyte);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Integer)
{
// Integer values come in as longs from the reader.
long val = (long)reader.Value;
// If the value fits in 8 bits, convert it to a signed byte.
if (val >= -128 && val <= 255)
{
return unchecked((sbyte)val);
}
// We got a value that can't fit in an sbyte.
throw new JsonSerializationException("Value was out of range for an sbyte: " + val);
}
// We got something we didn't expect, like a string or object.
throw new JsonSerializationException("Unexpected token type: " + reader.TokenType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Write sbyte values out in the same format we read them.
// Note this is technically invalid JSON per the spec.
writer.WriteRawValue("0x" + ((sbyte)value).ToString("X2"));
}
}
To use the converter, pass an instance of it to JsonConvert.DeserializeObject like this:
var deserialized = JsonConvert.DeserializeObject<List<MyData>>(JsonStr, new SByteConverter());
Working demo: https://dotnetfiddle.net/fEW6wy

JObject ToObject - Crashes on a "bad date" conversions

I'm using a JObject to handle my client post's.
I convert the JObject into a strong type entity using the ToObject function.
When the datetime value isn't valid - let's say 29\05\2014(since there aren't 29 months), I get an exception:
Could not convert string to DateTime: 29/05/2014. Path 'PurchaseDate.Value'.
I understand the exception and I would like to prevent crashes in those kind of situations.
How can I tell the JObject to ignore invalid date values? In my specific case my entity is a nullable datetime object so I would like to keep in null if the parsing fails(rather then crash).
In this specific case I'm talking about a datetime, but if someone can give me a more generic answer on how I can prevent failures on "invalid parsing\conversions" that would be great, since all of my entities contain nullable fields and I don't want to handle validations on the client side.
You cannot disable them just for invalid dates, but you can stop the parsing of date values, storing them as strings and implement a custom parsing later.
jObject.ToObject<MyObject>( new JsonSerializer {
DateParseHandling = DateParseHandling.None
});
I found a work around - Adding a converter:
var js = new JsonSerializer
{
DateParseHandling = DateParseHandling.DateTime,
};
js.Converters.Add(new DateTimeConverter());
dynamic jsonObject = new JObject();
jsonObject.Date = "29/05/2014";
var entty = ((JObject)jsonObject).ToObject<Entity>(js);
Definitions:
public class Entity
{
public DateTime? Date { get; set; }
}
public class DateTimeConverter : DateTimeConverterBase
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
DateTime val;
if (reader.Value != null && DateTime.TryParse(reader.Value.ToString(), out val))
{
return val;
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((DateTime)value).ToString("MM/dd/yyyy"));
}
}

Changing serialization of Geography in JSON.NET + WebApi2/OData

I have a simple ADO.NET Entity Model which I'm exposing using OData. One of the fields in the entity model is a Geography type (geography in SQL Server). I can query the data just fine, and I get the following serialized format for the geography columns:
"Shape":{
"WellKnownValue":{
"CoordinateSystemId":4326,
"WellKnownText":"POLYGON ((...)",
"WellKnownBinary":null
}
So this works, but I'm hoping I can change the serialization of this object to make it more like:
"Shape":"4326:POLYGON((...))"
Admittedly this is mostly for aesthetics, but it'd be nicer to have a simpler graph and shorter message too.
I wrote the following class which I thought would help:
public class JsonGeographyConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.Equals(typeof(DbGeography));
}
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)
{
var geog = (DbGeography)value;
if (geog != null)
writer.WriteValue(string.Format("{0}:{1}", geog.WellKnownValue.CoordinateSystemId, geog.WellKnownValue.WellKnownText));
else
writer.WriteNull();
}
}
And added it to the JSON serializer settings in my OData configuration:
var config = new HttpConfiguration();
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new JsonGeographyConverter());
But it doesn't seem to make a difference. In fact, a breakpoint placed in CanConvert is never reached, so I'm inclined to think that I'm not setting up JSON correctly.
I also tried:
var config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new JsonGeographyConverter());
but this also had no effect.
Hopefully someone can point out what I'm doing wrong?
Although Web API iteself uses the Json.Net serializer, a little digging around in the source code seems to indicate that the MediaTypeFormatter for Web API OData uses its own internal serializer which is not Json.Net. Therefore, adding a Json.Net converter to the configuration will not have any effect on OData. Unfortunately, without a deep-dive analysis of the code, I do not know whether OData's serializer is extensible in the same way, and/or whether it is possible to get it to use Json.Net instead.
I required some asthetics as well since I did not want to read into a json object when all I required was the latlng on the client side so I did the same.
My code is below. Been working for awhile now with no issues.
public class DbGeographyConverter : JsonConverter
{
public override bool CanConvert ( Type objectType )
{
return objectType.IsAssignableFrom( typeof( DbGeography ) );
}
public override object ReadJson ( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
{
if ( reader.Value == null ) {
return null;
}
return Parser.ToDbGeography( reader.Value.ToString() );
}
public override bool CanWrite { get { return true; } }
public override void WriteJson ( JsonWriter writer, object value, JsonSerializer serializer )
{
//Attempting to serialize null dosent go well
if ( value != null ) {
var location = value as DbGeography;
serializer.Serialize( writer, location.Latitude + "," + location.Longitude );
}
}
}

Categories

Resources