If I have the following XML segment:
<Times>
<Time>1/1/1900 12:00 AM</Time>
<Time>1/1/1900 6:00 AM</Time>
</Times>
What should the corresponding property look like that, when deserialization occurs, accepts the above XML into a list of DateTime objects?
This works to deserialize the XML segment to a list of string objects:
[XmlArray("Times")]
[XmlArrayItem("Time", typeof(string))]
public List<string> Times { get; set; }
But when I use DateTime as the type instead of string (for both the List type and XmlArrayItem type), I get the following error:
The string '1/1/1900 12:00 AM' is not a valid AllXsd value.
Thanks!
With DateTime, I expect that a large part of the problem is that the format of the xml is wrong; that isn't the xsd standard for dates... can you influence the xml at all? Otherwise, you might have to stick with strings and process it afterwards.
More standard xml would be:
<Times>
<Time>1900-01-01T00:00:00</Time>
<Time>1900-01-01T06:00:00</Time>
</Times>
For example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class Data
{
[XmlArray("Times")]
[XmlArrayItem("Time")]
public List<DateTime> Times { get; set; }
static void Main()
{
XmlReader xr = XmlReader.Create(new StringReader(#"<Data><Times>
<Time>1900-01-01T00:00:00</Time>
<Time>1900-01-01T06:00:00</Time>
</Times></Data>"));
XmlSerializer ser = new XmlSerializer(typeof(Data));
Data data = (Data) ser.Deserialize(xr);
// use data
}
}
The easiest way is to create a new property which is serialized instead of the Times property, and handles the formatting :
[XmlIgnore]
public IList<DateTime> Times { get; set; }
[XmlArray("Times")]
[XmlArrayItem("Time")]
public string[] TimesFormatted
{
get
{
if (this.Times != null)
return this.Times.Select((dt) => dt.ToString("MM/dd/yyyy hh:mm tt", CultureInfo.InvariantCulture)).ToArray();
else
return null;
}
set
{
if (value == null)
this.Times = new List<DateTime>();
else
this.Times = value.Select((s) => DateTime.ParseExact(s, "MM/dd/yyyy hh:mm tt", CultureInfo.InvariantCulture)).ToList();
}
}
Take a look at the msdn article:
http://msdn.microsoft.com/en-us/library/ms950721.aspx
It suggests that a DateTime object should be marked as such:
[System.Xml.Serialization.XmlElementAttribute("publication-date",
DataType="date")]
public System.DateTime publicationdate;
Related
Serializer settings:
jsonSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
jsonSettings.DateFormatString = "yyyy-MM-ddTHH:mm:ss.fffffffZ";
jsonSettings.DateParseHandling = DateParseHandling.DateTimeOffset;
jsonSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
Original json:
{
"value": "someData",
"startDate": "2021-01-16T00:00:00.000Z",
"endDate": "2021-01-18T00:00:00.000Z"
}
Class that I'm deserializing to:
public class MyData
{
[JsonConstructor]
public MyData(string value, string startDate, string endDate)
{
this.Value = value;
this.StartDate = startDate;
this.EndDate = endDate;
}
public string Value { get; }
public string StartDate { get; }
public string EndDate { get; }
}
Deserialization call:
using (FileStream fileStream = File.OpenRead(jsonFilePath))
using (StreamReader streamReader = new StreamReader(fileStream))
using (JsonTextReader jsonReader = new JsonTextReader(streamReader))
{
return this.JsonSerializer.Deserialize(jsonReader, typeof(MyData));
}
Okay, now ideally I would have hoped the deserializer would be smart enough to say, "Okay, I recognize that the value of this 'startDate' field in the input json is a DateTime string, but since the field I'm deserializing it to in the target type is a string, and not a DateTime or DateTimeOffset, I'm just going to leave the string alone and populate the corresponding fields with the exact same string as the input."
However, even if it did decide to convert the string to a DateTimeOffset object, then convert it back to a string during the deserialization process, shouldn't it use the explicitly provided DateFormatString in the settings for the deserialized value? Instead, this is what I'm seeing for the values of the StartDate and EndDate fields in my MyData instance:
myData.startDate == "01/16/2021 00:00:00 +00:00"
myData.endDate == "01/18/2021 00:00:00 +00:00"
Now before you mention it, yes I know I could just set the DateParseHandling setting to DateParseHandling.None, but these serializer setting are used not just for this one class but for lot's of other existing classes, and I don't know if making that change might adversely affect the behavior of some other part of the code.
So is there any way to tell the serializer, to use these settings when deserializing explicitly to DateTime or DateTimeOffset objects, or when deserializing to an arbitrary object without a defined type, but when deserializing explicitly to string fields to leave the input datetime strings unchanged?
Or, failing that, ist there a way to just tell the deserializer to explicitly use the specified DateFormatString when deserializing datetime strings to fields of type string?
So it turns out the problem was with a custom converter we are using for some outer classes that first parses the whole object to a JObject, and then does further deserialization from that JObject. I don't want to make too dramatic of changes to this custom converter as it may adversely affect the behavior of other parts of the codebase. So here is what I'm doing as a workaround.
First, I moved the date/time format string to a public constant:
public const string DefaultDateFormatString = "yyyy-MM-ddTHH:mm:ss.fffffffZ";
Then I modified the constructors on the class I was having problems with to explicitly set the date strings to my required format:
public class MyData
{
public MyData(string value, string startDate, string endDate)
{
this.Value = value;
this.StartDate = startDate;
this.EndDate = endDate;
}
[JsonConstructor]
public MyData(string value, DateTimeOffset startDate, DateTimeOffset endDate)
: this(
value,
startDate.ToString(JsonUtility.DefaultDateFormatString),
endDate.ToString(JsonUtility.DefaultDateFormatString))
{
}
public string Value { get; }
public string StartDate { get; }
public string EndDate { get; }
}
You could also do like this:
JObject my_jobject = JsonConvert.DeserializeObject<JObject>(my_jsonStr,
new JsonSerializerSettings {DateParseHandling = DateParseHandling.None});
My asp core api gets as input object with the following json structure:
{
"id": 1,
"zoneDate": {
"zone": "Paris/France",
"dateTime": "2019-04-02T00:00:00"
}
}
I wanna convert this object to the following model:
public class Model
{
public int Id { get; set; }
public DateTime DateTime { get; set; }
}
I have a service that knows how to convert complex json object zoneDate to build in .Net DateTime with an offset.
I need api to do the conversion automatically (in model binding) whenever it sees DateTime in the model, which is marked with some custom attribute (or built in if any). Any ideas to do that elegantly? Is there something like TypeConverter that can work on single simple type property and that can take parameter injected through constructor? Any help is deeply appreciated!
EDIT:
I need the binding on the property level, not on the model level. I mean if I use model binder, then I need to create it for EACH and EVERY new model which have DateTime props that should be created from ZoneDate json. That's not what I want. I want to treat ZoneDate as simple type, when I convert it to DateTime which has an attribute or some name convention
UPDATE:
Finally, the #jpgrassi advice was implemented.
Instead of writing these binders, that just hide the complexity away..
why not introduce a type ZoneDate that contains those fields and let
the default modelbinder do its work? In the end, what will you gain
from accepting that format, and binding it yourself? I think having a
type is way better. You let the compiler help you with stuff
(refactoring and etc). Plus your code is simple.
First of all Paris/France is not valid Timezone text format, the correct should be Europe/Paris.
Then I assume you want to convert the given the time
using Newtonsoft.Json.Linq;
using System;
using System.Globalization;
using TimeZoneConverter;
namespace ParseJsonDateTimeZone
{
class Program
{
static void Main(string[] args)
{
string json = "{\"id\": 1,\"zoneDate\":{\"zone\":\"Europe/Paris\",\"dateTime\":\"2019-04-02T00:00:00\"}}";
Console.WriteLine(new Model(json));
}
public class Model
{
public Model(string json)
{
JObject jObject = JObject.Parse(json);
Id = (int) jObject["id"];
JToken zoneDate = jObject["zoneDate"];
TimeZoneInfo tzi;
try
{
tzi = TZConvert.GetTimeZoneInfo(zoneDate["zone"].ToString());
}
catch(TimeZoneNotFoundException)
{
// Fallback to UTC or do something else?
tzi = TZConvert.GetTimeZoneInfo("UTC");
}
CultureInfo culture = CultureInfo.InvariantCulture;
DateTime dateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(zoneDate["dateTime"].ToString()), tzi);
DateTime = TimeZoneInfo.ConvertTime(dateTime, tzi );
}
public int Id { get; set; }
public DateTime DateTime { get; set; }
}
}
}
I would like serialize object to XML. This part is easy.
But i would serialize properties (like DateTime) with a specific format.
My current code is this one :
[XmlIgnore]
private DateTime _paymentDate { get; set; }
public string PaymentDate
{
get
{
return this._paymentDate.ToString("yyyy-MM-dd HH:mm:ss");
}
set
{
this._paymentDate = DateTime.ParseExact(value, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
}
}
But it's very bad. If i have 100 DateTime properties, i should duplicate this code 100 times.
So, i would like to use a formatter (like with Newtonsoft JSON).
How can i do it ?
Thanks a lot :)
I have a service which returns a json like this :
[
{
"title":"First event",
"startDate":"\/Date(1495512000000-0400)\/",
"endDate":"\/Date(1495857540000-0400)\/",
}
]
I deserialize the json output to the list with respect to the model I have defined :
Model
public class EventModel
{
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("startDate")]
public DateTime StartDate { get; set; }
[JsonProperty("endDate")]
public DateTime EndDate { get; set; }
}
C# Code of deserialization
I am using Newtonsoft.Json package for deserialization.
List< EventModel > lstEvent = new List< EventModel >();
lstEvent = JsonConvert.DeserializeObject<List< EventModel >>(await objMyServices.GetData());
So in this case when I debug I get the following output in two different timezone:
For GMT+5:30 (Kolkata - India):
Start Time : 23-05-2017 09:30:00
End Time : 27-05-2017 09:29:00
For GMT-4:00 (New York):
Start Time : 23-05-2017 00:00:00
End Time : 26-05-2017 23:59:00
Therefore for every record, start time is not parsed to the respective system time zone when I switch to GMT-4:00 (New York).
What is the thing I am missing here ?
Json.NET allows you to specify what kind of DateTime to return (UTC, Local) through the DateTimeZoneHandling serializer setting. The default is Local.
To return a UTC DateTime, use DateTimeZoneHandling.Utc :
var settings = new Newtonsoft.Json.JsonSerializerSettings()
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc
};
var content=await objMyServices.GetData();
var lstEvent = JsonConvert.DeserializeObject<List<EventModel>>(content, settings);
Perhaps a better option is to change the property types to DateTimeOffset. This type preserves the original offset.
You can use Ticks in Web API for this. This is much convenient when you are working with multiple time zones. Refer below example .
DateTime.UtcNow.Ticks
I have some troubles with an XML Serialization. (Probably something similar to THIS SO QUESTION, but regarding serialization instead of deserialization).
I need to serialize a List<DateTime>, using a custom DateTime format.
I'm able to do it for a single DateTime variable, but I can't figure out how to do it for a list.
Here is what I'm trying to do in my code.
For the simple DateTime variable I use this code:
[XmlType("Time")]
public class Time : BaseClass
{
public Time()
{
base.Name = string.Empty;
StartTimeDt = DateTime.Now;
}
[XmlIgnore]
public DateTime StartTimeDt
{
get { return DateTime.Parse(StartTime); }
set { StartTime = value.ToString("yyyy-MM-dd HH:mm:ss.fff"); }
}
[XmlAttribute("StartTime")]
public string StartTime { get; set; }
}
to obtain this XML structure:
<Time Name="" StartTime="2014-05-05 11:00:00.000">
When I create a new instance of Time class and I serialize it, I obtain exatcly the XML structure I expect, with my StartTime variable serialized with the custom datetime format.
When I have a List<DateTime>, I try to use this kind of code:
[XmlType("Calendar")]
public class Calendar : BaseClass
{
public Calendar()
{
base.Name = string.Empty;
DaysDt = new List<DateTime>();
}
[XmlIgnore]
public List<DateTime> DaysDt
{
get { return Days.Select(item => DateTime.Parse(item)).ToList(); }
set { Days = value.Select(item => item.ToString("yyyy-MM-dd")).ToList(); }
}
[XmlArrayItem("Day", typeof(string))]
public List<string> Days { get; set; }
}
to generate this XML output:
<Calendar Name="">
<Days>
<Day>2014-03-02</Day>
<Day>2014-05-03</Day>
<Day>[…]</Day>
</Days>
</Annual>
But I'm doing something obviously wrong, because when I create a new instance of Calendar class and I try to add some datetimes
Calendar calendar = new Calendar();
calendar.DaysDt = new List<DateTime>();
calendar.DaysDt.Add(DateTime.Now);
calendar.DaysDt.Add(DateTime.Now.AddDays(5));
calendar.DaysDt.Add(DateTime.Now.AddDays(10));
both my DaysDt and Days lists always contain 0 object, and when I go for serialization it serializes nothing...
Any noticeable error in this sample code? I'm quite sure I'm making some errors in the transition between the List<DateTime> and the List<string> with the custom format...
There is a mistake on the code you posted.
It is reasonable that nothing serializes. The DaysDt object is not serialized (because you set the XmlIgnore attribute) and you never assign any values on the Days object. I presume you may have mixed up getting/setting with adding items to a list.
When you execute:
calendar.DaysDt.Add(DateTime.Now);
The setter is not called!
So that leaves you with an empty Days object.
Try this code for example and see if it works:
Calendar calendar = new Calendar();
List<DateTime> lst = new List<DateTime>();
lst.Add(DateTime.Now);
lst.Add(DateTime.Now.AddDays(5));
lst.Add(DateTime.Now.AddDays(10));
calendar.DaysDt = lst;