Parsing ISO Duration with JSON.Net - c#

I have a Web API project with the following settings in Global.asax.cs:
var serializerSettings = new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateTimeZoneHandling = DateTimeZoneHandling.Utc
};
serializerSettings.Converters.Add(new IsoDateTimeConverter());
var jsonFormatter = new JsonMediaTypeFormatter { SerializerSettings = serializerSettings };
jsonFormatter.MediaTypeMappings.Add(GlobalConfiguration.Configuration.Formatters[0].MediaTypeMappings[0]);
GlobalConfiguration.Configuration.Formatters[0] = jsonFormatter;
WebApiConfig.Register(GlobalConfiguration.Configuration);
Despite all this, Json.Net cannot parse ISO durations.
It throws this error:
Error converting value "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z" to
type 'System.TimeSpan'.
I'm using Json.Net v4.5.
I've tried different values such as "P1M" and others listed on the wiki page with no luck.
So the question is:
Am I missing something?
Or do I have to write some custom formatter?

I ran into the same problem and am now using this custom converter to Convert .NET TimeSpans to ISO 8601 Duration strings.
public class TimeSpanConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var ts = (TimeSpan) value;
var tsString = XmlConvert.ToString(ts);
serializer.Serialize(writer, tsString);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var value = serializer.Deserialize<String>(reader);
return XmlConvert.ToTimeSpan(value);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof (TimeSpan) || objectType == typeof (TimeSpan?);
}
}

Related

How to deserialize EPOCH using Newtonsoft

I need to deserialize a JSON using Newtonsoft.Json, one of my values in my JSON has an EPOCH format:
*"fechaGrabacionODH": {
"$date": 1634689986082
},*
Im using this to Deserialize, but it's not working
*var settings = new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,
};*
*Data = JsonConvert.DeserializeObject<Constructores_IdPnODH.Data>(payload_Decryp, settings);**
I'm getting this error:
Unexpected character encountered while parsing value: {. Path 'fechaGrabacionODH', line 1, position 261.
I really appreciate any help.
Thanks in advance.
You can use a custom JsonConverter
public class EpochConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var jObject = new JObject();
jObject["$date"] = new DateTimeOffset((DateTime)value).ToUnixTimeMilliseconds();
jObject.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var epoch = jObject.GetValue("$date").Value<long>();
return DateTimeOffset.FromUnixTimeMilliseconds(epoch).UtcDateTime;
}
public override bool CanRead => true;
public override bool CanConvert(Type objectType) => objectType == typeof(DateTime);
}
And then apply it with an attribute
class YourObject{
[JsonConverter(typeof(EpochConverter))]
public DateTime fechaGrabacionODH;
}
dotnetfiddle

json.net: DateTimeStringConverter gets an already DateTime converted object in ReadJson()

Prerequisites:
JSON.Net 11.0.2
I need to store the UTC DateTime round-trip date/time pattern via a JSON based REST-API.
string utcTimestamp = DateTime.UtcNow.ToString( "o" );
// 2018-11-27T22:35:32.1234567Z
So I wrote myself a DateTimeStringConverter to ensure no local culture information gets involved.
class DateTimeStringConverter:
JsonConverter<DateTime>
{
public override void WriteJson( JsonWriter writer, DateTime value, JsonSerializer serializer )
{
string convertedValue = value.ToString( "o" );
writer.WriteValue( convertedValue );
}
public override DateTime ReadJson( JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer )
{
string value = reader.Value.ToString( );
DateTime convertedValue = DateTime.Parse( value ).ToLocalTime( );
return convertedValue;
}
}
I was very confused as to why I was getting a DateTime object without the milliseconds. After a lot of trial and error, I got into it.
public override DateTime ReadJson( JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer )
{
Console.WriteLine( reader.Value.GetType( ).ToString( ) );
// System.DateTime
string value = reader.Value.ToString( );
DateTime convertedValue = DateTime.Parse( value ).ToLocalTime( );
return convertedValue;
}
JSON.Net serves me an already converted DateTime object to deserialize on my own without any millisecond data. I didn't find any clues as to whether it is a bug or a feature.
To counter check it I wrote a BooleanStringConverter.
class BoolStringConverter:
JsonConverter<bool>
{
public override void WriteJson( JsonWriter writer, bool value, JsonSerializer serializer )
{
string convertedValue = false == value ? "False" : "True";
writer.WriteValue( convertedValue );
}
public override bool ReadJson( JsonReader reader, Type objectType, bool existingValue, bool hasExistingValue, JsonSerializer serializer )
{
Console.WriteLine( reader.Value.GetType( ).ToString( ) );
// System.String
string value = ( string ) reader.Value;
bool convertedValue = "False" == value ? false : true;
return convertedValue;
}
}
JSON.Net doesn't serve me an already converted bool object.
Is it a bug or is it a feature?
This is a known behavior of Json.Net. Since JSON does not have a built-in syntax for denoting dates (like it does for booleans), they have to be represented as strings. By default Json.Net tries to be nice and parse date-looking strings for you.
If you are using your own converter for dates, or otherwise want to handle date parsing yourself, you need to be sure to set the DateParseHandling setting to None in the JsonSerializerSettings, otherwise the internal reader will try to handle it first.
JsonSerializerSettings settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new DateTimeStringConverter() },
DateParseHandling = DateParseHandling.None
};
var foo = JsonConvert.DeserializeObject<Foo>(json, settings);
We can cast the DateTime value directly to DateTimeOffset, like so: (DateTimeOffset) reader.Value;.
Full code:
public class DateTimeConverter : Newtonsoft.Json.JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTimeOffset);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value == null) return null;
var deviceTime = (DateTimeOffset) reader.Value;
return deviceTime.DateTime;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue((DateTime)value);
}
}

How to Bind a json to an Interface collection, and vice versa within a model using MVC.Net

Update: due the answer, i found old title irrelevant and change it from the 'How to Implement Custom JsonConverter that uses JsonConvert' to what currently it is.
I'm in very tight and time limit situation, and i have to do as what the title says...
i tried many ways till morning, model doesn't bind, the method similar to FormCollection in MVC returns nulls, and so on and so on, since i add a list of interfaces to one of my model, so i need to handle it,... morning client report bugs, and their system is under pressure, so i can't help it but work till it fix.
here's my current code:
What happen? once the writer method returns server returns 500 internal server error...
naturally they do this using serializer object, but i don't know how to do this customization using serializer, so i really in need to call this method which accept a serializer setting.
public class CollectionInterfaceConverterForModels<TInterface> : JsonConverter
{
private readonly TypeNameSerializationBinder Binder;
public CollectionInterfaceConverterForModels()
{
Binder = new TypeNameSerializationBinder("Cartable.Models.{0}, Cartable");
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IList<TInterface>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//IList<TInterface> items = serializer.Deserialize<List<TInterface>>(reader).Cast<TInterface>().ToList();
//return items;
var json = existingValue.ToString();
if (json == null)
json = "";
return JsonConvert.DeserializeObject<List<TInterface>>(json,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = Binder
});
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//serializer.Serialize(writer, value, typeof(IList<T>));
string serializeObject = JsonConvert.SerializeObject(value, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = Binder
});
writer.WriteRaw(serializeObject);
writer.Flush();
}
}
Model sample:
public class IncomingFaxesVM
{
public long Code { get; set; }
.
.
.
[JsonConverter(typeof(CollectionInterfaceConverterForModels<IMetadata>))]
public List<IMetadata> Metadata { get; set; }
}
Yet, i'm unable to do what i asked, but i solved the issue using another way,
So if you find the answer, i mark yours, other wise i change the title to interface binding so other user can find it easily, and here's my answer to solve the issue.
public class CollectionInterfaceConverterForModels<TInterface> : JsonConverter
{
private readonly TypeNameSerializationBinder Binder;
public CollectionInterfaceConverterForModels()
{
Binder = new TypeNameSerializationBinder("Cartable.Models.{0}, Cartable");
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IList<TInterface>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var currentBinder = serializer.Binder;
var currentTypeNameHandling = serializer.TypeNameHandling;
serializer.TypeNameHandling = TypeNameHandling.Auto;
serializer.Binder = Binder;
IList<TInterface> items = serializer.Deserialize<List<TInterface>>(reader).ToList();
serializer.TypeNameHandling = currentTypeNameHandling;
serializer.Binder = currentBinder;
return items;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//The reason store these, is to leave system unchanged, in case it customized on general section by another programmer
var currentBinder = serializer.Binder;
var currentTypeNameHandling = serializer.TypeNameHandling;
serializer.TypeNameHandling=TypeNameHandling.Auto;
serializer.Binder = Binder;
serializer.Serialize(writer, value, typeof(List<TInterface>));
serializer.TypeNameHandling = currentTypeNameHandling;
serializer.Binder = currentBinder;
}
}

Is it possible to serialize DateTimeOffset to zulu time string with Json.NET?

I have a DateTimeOffset object of "05/06/2014 05:54:00 PM -04:00".
When serializing using Json.NET and ISO setting, I get "2014-05-06T17:54:00-04:00".
What I would like to have is the UTC/Zulu version of that string "2014-05-06T21:54:00Z".
However, I could not find any serializer setting to achieve this. I know for DateTime serialization, I can set DateTimeZoneHandling = DateTimeZoneHandling.Utc to have the Zulu format. However, there isn't such setting option for DateTimeOffset. Am I missing something? Or do I have to create a custom override for this?
Try using the IsoDateTimeConverter that comes with Json.Net:
var date = new DateTime(2014, 5, 6, 17, 24, 55, DateTimeKind.Local);
var obj = new { date = new DateTimeOffset(date) };
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new IsoDateTimeConverter
{
DateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ",
DateTimeStyles = DateTimeStyles.AdjustToUniversal
});
string json = JsonConvert.SerializeObject(obj, settings);
Console.WriteLine(json);
Output:
{"date":"2014-05-06T22:24:55Z"}
You can write a custom converter like this:
class UtcDateTimeOffsetConverter : Newtonsoft.Json.Converters.IsoDateTimeConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is DateTimeOffset)
{
var date = (DateTimeOffset)value;
value = date.UtcDateTime;
}
base.WriteJson(writer, value, serializer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object value = base.ReadJson(reader, objectType, existingValue, serializer);
if (value is DateTimeOffset)
{
var date = (DateTimeOffset)value;
value = date.ToLocalTime();
}
return value;
}
}
And apply it on the property you want using the JsonConverter attribute:
[JsonConverter(typeof(UtcDateTimeOffsetConverter))]
public DateTimeOffset Date { get; set; }

Deserialization issue for datetime field

What is the best way to deserialize an JSON
I have the following JSON
"_created" : {
"$dt": "2013-03-26T16:45:20Z"
}
and i want get field of object, like this - DataTime Created {get; set;}
Question for json.net http://james.newtonking.com/projects/json-net.aspx experts
Easiest way is creating JsonConvertor
public class MongoDbDateTimeConverter : DateTimeConverterBase
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
return jObject["$dt"].Value<DateTime>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
and used it with property
[JsonConverter(typeof(MongoDbDateTimeConverter))]
public DateTime Created { get; set; }
You could use the JSON serializer/deserializer from NewtonSoft, worked ok for me.
Nuget package
MediaTypeFormatter example
Add following MediaTypeFormatter to the GlobalConfiguration like so:
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonFormatter());
MediaTypeFormatter:
public class JsonFormatter : MediaTypeFormatter
{
private const string WesternEuropeStandardTime = "W. Europe Standard Time";
private TimeZoneInfo timeZoneInfo;
public JsonFormatter()
{
SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
this.timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(WesternEuropeStandardTime);
}
public override bool CanReadType(Type type)
{
return true;
}
public override bool CanWriteType(Type type)
{
return true;
}
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, System.Net.Http.HttpContent content, IFormatterLogger formatterLogger)
{
Task<object> task = Task<object>.Factory.StartNew(() =>
{
JsonSerializerSettings settings = new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
};
StreamReader sr = new StreamReader(readStream);
JsonTextReader jreader = new JsonTextReader(sr);
JsonSerializer ser = new JsonSerializer();
ser.Converters.Add(new DateTimeConverter(this.timeZoneInfo) { DateTimeFormat = "o" });
return ser.Deserialize(jreader, type);
});
return task;
}
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
Task task = Task.Factory.StartNew(() =>
{
JsonSerializerSettings settings = new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
};
string json = JsonConvert.SerializeObject(
value,
Formatting.Indented,
new JsonConverter[1] { new DateTimeConverter(this.timeZoneInfo) { DateTimeFormat = "o" } });
byte[] buf = System.Text.Encoding.Default.GetBytes(json);
writeStream.Write(buf, 0, buf.Length);
writeStream.Flush();
});
return task;
}
private class DateTimeConverter : IsoDateTimeConverter
{
private TimeZoneInfo timeZoneInfo;
public DateTimeConverter(TimeZoneInfo timeZoneInfo)
{
this.timeZoneInfo = timeZoneInfo;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
DateTime? date = value as DateTime?;
if (date.HasValue && DateTime.MinValue != date.Value && DateTime.MaxValue != date.Value)
{
TimeSpan timeZoneOffset = this.timeZoneInfo.GetUtcOffset(date.Value);
value = DateTime.SpecifyKind(date.Value - timeZoneOffset, DateTimeKind.Utc);
}
base.WriteJson(writer, value, serializer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object result = base.ReadJson(reader, objectType, existingValue, serializer);
DateTime? date = result as DateTime?;
if (date.HasValue && DateTime.MinValue != date.Value && DateTime.MaxValue != date.Value)
{
TimeSpan timeZoneOffset = this.timeZoneInfo.GetUtcOffset(date.Value);
result = DateTime.SpecifyKind(date.Value + timeZoneOffset, DateTimeKind.Utc);
}
return result;
}
}
}
if you are using json.NET
try to serialize a datetime field or an object consisting of datetime field like this:
JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
};
string serializedObject= Newtonsoft.Json
.JsonConvert
.SerializeObject(data, microsoftDateFormatSettings);
it works without a glitch, if serialization has been done using JSON.NET :)
and then you can successfully deserialize it back
var myobject = Newtonsoft.Json.JsonConvert.DeserializeObject(serializedObject);
If you're asking about a JSON parser in C#, then that is more of a Google thing. Go search there. There are loads of them available.
If you're asking about how to parse this string into DateTime, then DateTime.TryParse() will help you. You can help it by supplied string format, something like yyyy-MM-ddTHH:mm:ss.

Categories

Resources