JSON.NET deserialize custom date format - c#

I want to deserialize date like this "20160101000000000" to DateTime in UTC kind.
var data = "20160101000000000";
var dateTime = JsonConvert.DeserializeObject<DateTime>(data,
new IsoDateTimeConverter { DateTimeFormat = "yyyyMMddhhmmssfff"});

Try following
var data = "20160101000000000";
var dateTime = new DateTime(JsonConvert.DeserializeObject<Int64>(data));
If you want to UTC format, try following
var utc = dateTime.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'");
Or implement Custom Datetime Converter
public class CustomDateTimeConverter : DateTimeConverterBase
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{ return; }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return new DateTime(Convert.ToInt64(reader.Value));
}
}
And use like this
var data = "20160101000000000";
var dateTime = JsonConvert.DeserializeObject<DateTime>(data, new CustomDateTimeConverter());

Remember that in JSON, strings must be quoted. Therefore what you've got in data is actually not a JSON string (it's a number). If you want JSON.NET to parse this the way you expect, you'll need to wrap it in quotes:
var data = "\"20160101000000000\"";

Related

Read raw Date string in a Custom JsonConverter of JSON.net

I have an input date string in ISO 8601 format (2020-07-23T09:42:02.694Z). And in my Controller the Property is defined as DateTime. I have written a custom date converter to convert the date to LocalTime zone (this is unusual).
public class UTCtoUnspecifiedDateConverter : IsoDateTimeConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader != null
&& reader.Value != null
&& this.CanConvert(objectType)
&& this.CanRead
&& typeof(DateTime) == reader.Value.GetType())
{
return DateTime.SpecifyKind((DateTime)reader.Value, DateTimeKind.Unspecified).ToLocalTime();
}
else
{
return base.ReadJson(reader, objectType, existingValue, serializer);
}
}
}
Is there any way to get the raw string that I am sending from the Client in this converter?
Basically I want to check if there is a Z at the end or not.
While debugging I saw a private property on the reader which stores this information. Any way to get this here?
If you are using a custom JsonConverter to handle dates, you need to set the DatePraseHandling setting to None.
var jsonSettings = config.Formatters.JsonFormatter.SerializerSettings;
jsonSettings.Converters.Add(new UTCtoUnspecifiedDateConverter());
jsonSettings.DateParseHandling = DateParseHandling.None;
Then, in your converter, you will get date values from the reader as strings which you can parse using DateTime.TryParse or DateTime.TryParseExact.
As an aside, there are some checks you are making inside of ReadJson that are not necessary. You shouldn't need to call CanConvert or CanRead, because Json.Net does that for you in order to determine whether to call ReadJson in the first place. Also, the reader passed to ReadJson will never be null, so you don't need to check for that either. So that simplifies the code quite a bit.
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value is string &&
DateTime.TryParseExact((string)reader.Value, DateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date))
{
return DateTime.SpecifyKind(date, DateTimeKind.Unspecified).ToLocalTime();
}
else
{
return base.ReadJson(reader, objectType, existingValue, serializer);
}
}

How do I deserialize a high-precision decimal value with Json.NET?

I want to deserialize JSON containing long decimal values into custom types to maintain their precision (i.e., a custom BigDecimal class). I'm using Json.NET 9.0.1 and .NET 4.6.1. I've tried using a JsonConverter, but it seems that the value available when ReadJson is called has already been identified and read by Json.NET as a .NET decimal type and is limited to its precision.
Ideally I would have access to the raw string so I could put it in a custom type. I can use string properties on the target object and it deserializes the full string, but then I'd have to further process the object (i.e., copy it into another representation) and that's especially messy across a large schema.
Any thoughts on a better approach?
Here's the target class:
public class DecimalTest
{
public string stringValue { get; set; }
public decimal decimalValue { get; set; }
public BigDecimal bigDecimalValue { get; set; }
}
Here's a test with JSON:
[TestMethod]
public void ReadBigDecimal_Test()
{
var json = #"{
""stringValue"" : 0.0050000012852251529693603515625,
""decimalValue"" : 0.0050000012852251529693603515625,
""bigDecimalValue"" : 0.0050000012852251529693603515625
}";
var settings = new JsonSerializerSettings();
settings.FloatParseHandling = FloatParseHandling.Decimal;
settings.Converters.Add(new JsonBigDecimalConverter());
var result = JsonConvert.DeserializeObject<DecimalTest>(json, settings);
Assert.IsNotNull(result);
Assert.AreEqual("0.0050000012852251529693603515625", result.stringValue);
Assert.AreEqual(0.0050000012852251529693603516m, result.decimalValue);
// *** This case fails ***
Assert.AreEqual("0.0050000012852251529693603515625", result.bigDecimalValue.ToString());
}
Here's the custom converter:
public class JsonBigDecimalConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(BigDecimal));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// *** reader.Value here already appears to be a .NET decimal.
// *** If I had access to the raw string I could get this to work.
return BigDecimal.Parse(reader.Value.ToString());
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Could you try if the following implementation of ReadJson works as you expect:
public override object ReadJson(
JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
return BigDecimal.Parse(token.ToString());
}
Update
Unfortunately the above won't work. There seems to be no way to read the raw string from the JSON data.
Also note that in my tests the assert for stringValue fails first. See this working example: https://dotnetfiddle.net/s0pqg3
I assume this is because Json.NET internally immediately parses any number token it encounters according to the specified FloatParseHandling. The raw data is never preserved.
I think the only solution is to wrap the big decimal string in quotes like so:
"bigDecimalValue" : "0.0050000012852251529693603515625"
Here is a working example that does exactly that in order to preserve the desired precision: https://dotnetfiddle.net/U1UG3z

How to get the current IPrincipal in custom JsonConverter?

In my ASP.NET Web API application I'm building a custom JsonConverter to convert any DateTime string in Json objects to DateTimeOffset, using the logged in User's offset.
I have read in this article that the IPrincipal is being reset during the serialization process.
A solution was offered in this article, which I implemented in my application.
What the author failed to mention is how to actually retrieve the IPrincipal inside of the ReadJson() method of the JsonConverter.
So I am currently stuck here.
internal class CustomDateTimeOffsetConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
if (value is DateTimeOffset)
{
var date = (DateTimeOffset)value;
value = date.LocalDateTime;
}
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
if (existingValue is DateTimeOffset)
{
// How to get the current logged in IPrincipal?
// var user = ...
}
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
Try this
var requestContext = System.Web.DynamicData.DynamicDataRouteHandler.GetRequestContext(System.Web.HttpContext.Current);
var user = requestContext.HttpContext.User;

Creating a custom JsonConverter to handle System.Text.Encoding objects

I have written a custom JsonConverter which I am hoping will allow me to serialize and deserialize Encoding objects within my classes:
public class EncodingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsSubclassOf(typeof(Encoding));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((Encoding)value).EncodingName);
}
public override bool CanRead { get { return true; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var name = reader.ReadAsString();
return Encoding.GetEncoding(name);
}
}
However, when I run the following test code, I get an exception when calling DeserializeObject, and the ReadJson method never gets called.
class Program
{
private static void Main(string[] args)
{
var test = new TestClass();
var jsonSettings = new JsonSerializerSettings
{
Converters = new[] { new EncodingConverter(), }
};
var json = JsonConvert.SerializeObject(test, jsonSettings);
var test2 = JsonConvert.DeserializeObject<TestClass>(json, jsonSettings);
}
}
class TestClass
{
public string Property1;
public Encoding Encoding = Encoding.UTF8;
}
The exception message is:
Target type System.Text.Encoding is not a value type or a non-abstract class.
Am I missing something?
There are three problems with your converter that I see.
You are using the wrong check in CanConvert().
You are using the wrong name for the Encoding when serializing.
You are using the wrong method to get the value from the reader when deserializing.
Let's take these one at a time.
First, in your CanConvert method you are using objectType.IsSubclassOf(typeof(Encoding)) to determine whether the converter should handle the Encoding. This works fine on serialization because you have a concrete instance of the encoding (e.g. UTF8Encoding), which is indeed a subclass of Encoding. However, on deserialization, the deserializer doesn't know what concrete type of encoding you are going to make, so the type that is passed to the converter is just Encoding. Since Encoding is not a subclass of itself, CanConvert returns false, and your ReadJson method never gets called. That leaves Json.Net to try to instantiate the Encoding itself, which it can't do (because Encoding is abstract), so it throws the error you mentioned in your question. You should instead use typeof(Encoding).IsAssignableFrom(objectType) inside your CanConvert method.
Second, when serializing the Encoding inside WriteJson, you are outputting the EncodingName property, which is the human-readable display name of the encoding, not the code page name. If you look at the documentation for the Encoding.GetEncoding(string) method, it says:
Parameters
name
Type: System.String
The code page name of the preferred encoding. Any value returned by the WebName property is valid. Possible values are listed in the Name column of the table that appears in the Encoding class topic.
So, you should be outputting the value of the WebName property in your WriteJson method if you want to be able to use this value to later reconstruct the Encoding in ReadJson.
Third, in your ReadJson method you are using reader.ReadAsString() to attempt to get the encoding name from the JSON. This will not work as you expect. When ReadJson is called by Json.Net, the reader is already positioned at the current value. When you call ReadAsString(), that advances the reader to the next token and then attempts to interpret that token as a string. What you really want to do is just get the value of the current token, which you can do using the Value property. Because Value is of type object, you will need to cast it to a string.
Here is the corrected code for the converter:
public class EncodingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Encoding).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((Encoding)value).WebName);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return Encoding.GetEncoding((string)reader.Value);
}
}
Fiddle: https://dotnetfiddle.net/UmLynX
Try:
public class CustomConverter : JsonConverter
{
public override bool CanConvert(System.Type objectType)
{
return true;// objectType.IsAssignableFrom(typeof(Encoding));
}
public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer)
{
return Encoding.GetEncoding(Convert.ToString(reader.Value));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var t = (Test)value;
var e = (Encoding)t.MyProperty;
writer.WriteValue(e.BodyName);
//serializer.Serialize(writer, e.BodyName);
}
}
And in Main:
var o = new Test { MyProperty = Encoding.UTF8 };
var s = new JsonSerializerSettings
{
Converters = new[] { new CustomConverter() }
};
var v = JsonConvert.SerializeObject(o, s);
var o2 = new Test();
o2.MyProperty = Encoding.GetEncoding(JsonConvert.DeserializeObject(v, s).ToString());

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"));
}
}

Categories

Resources