Reputation: 86947
The DateTimeOffset
property I have in this class doesn't get rendered when the data is represented as Xml. What do I need to do to tell the Xml serialization to render that proper as a DateTime
or DateTimeOffset
?
[XmlRoot("playersConnected")]
public class PlayersConnectedViewData
{
[XmlElement("playerConnected")]
public PlayersConnectedItem[] playersConnected { get; set; }
}
[XmlRoot("playersConnected")]
public class PlayersConnectedItem
{
public string name { get; set; }
public DateTimeOffset connectedOn { get; set; } // <-- This property fails.
public string server { get; set; }
public string gameType { get; set; }
}
and some sample data...
<?xml version="1.0" encoding="utf-8"?>
<playersConnected
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<playerConnected>
<name>jollyroger1000</name>
<connectedOn />
<server>log1</server>
<gameType>Battlefield 2</gameType>
</playerConnected>
</playersConnected>
I'm hoping there might be a way through an Attribute which I can decorate on the property...
Any way to get rid of those two namespaces declared in the root node? Should I?
Upvotes: 31
Views: 20473
Reputation: 1764
To supplement @Peter's answer, if you're using an ADO.NET Entities model (.edmx), and hence all the access modifiers are generated automatically in partial classes, you can edit the T4 template (expand the .edmx file in the solution explorer and open YourEdmxFilename.tt) to make it generate DateTimeOffset types with the internal
modifier instead of the default.
Simply replace the Property(EdmProperty)
method with the one below:
public string Property(EdmProperty edmProperty)
{
string typeName = _typeMapper.GetTypeName(edmProperty.TypeUsage);
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
typeName == "System.DateTimeOffset" || typeName == "Nullable<System.DateTimeOffset>" ? "internal" : Accessibility.ForProperty(edmProperty),
typeName,
_code.Escape(edmProperty),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}
Upvotes: -1
Reputation: 5728
It's a few years late, but here's the quick and easy way to completely serialize DateTimeOffset
using ISO 8601:
[XmlElement("lastUpdatedTime")]
public string lastUpdatedTimeForXml // format: 2011-11-11T15:05:46.4733406+01:00
{
get { return lastUpdatedTime.ToString("o"); } // o = yyyy-MM-ddTHH:mm:ss.fffffffzzz
set { lastUpdatedTime = DateTimeOffset.Parse(value); }
}
[XmlIgnore]
public DateTimeOffset lastUpdatedTime;
Upvotes: 42
Reputation: 118
one way to solve this problem is have your class implement the interface IXmlSerializable
.
Implementing this interface forces the serializer to call the 'overridden' WriteXml
and ReadXml
mathods.
something like that :
public void WriteXml(XmlWriter w)
{
wr.WriteStartElement("playersConnected");
w.WriteElementString("name", Name);
w.WriteElementString("connected-on" , ConnectedOn.ToString("dd.MM.yyyy HH:mm:ss"));
//etc...
}
and when you read it :
DateTimeOffset offset;
if(DateTimeoffset.TryParse(reader.Value, out offset))
{
connectedOn = offset;
}
it is a hassle but I cannot thing of any other way. also this solution gives you full control on your serialization process(this is the upside)
if you like this solution and want the complete one please comment and i will write it down
regarding the namespaces - i don't think you can get rid of it(i won't get the bonus score probably).
Upvotes: 2
Reputation: 694
I have found the solution here: http://tneustaedter.blogspot.com/2012/02/proper-way-to-serialize-and-deserialize.html
Replacing XmlSerializer with DataContractSerializer works awesome. See sample code below:
public static string XmlSerialize(this object input)
{
using (MemoryStream stream = new MemoryStream())
{
DataContractSerializer serializer = new DataContractSerializer(input.GetType());
serializer.WriteObject(stream, input);
return new UTF8Encoding().GetString(stream.ToArray());
}
}
public static T XmlDeserialize<T>(this string input)
{
using (MemoryStream memoryStream = new MemoryStream(new UTF8Encoding().GetBytes(input)))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
return (T)serializer.ReadObject(memoryStream);
}
}
Upvotes: 4
Reputation: 70128
I came up with this struct which implements XML serialization based on ISO 8601 formatting (e.g. 2011-11-11T15:05:46.4733406+01:00
). Hint: Trying to parse a DateTime
value such as 2011-11-11T15:05:46
fails as expected.
Feedback welcome. I didn't include the unit tests here because that would be too much text.
/// <remarks>
/// The default value is <c>DateTimeOffset.MinValue</c>. This is a value
/// type and has the same hash code as <c>DateTimeOffset</c>! Implicit
/// assignment from <c>DateTime</c> is neither implemented nor desirable!
/// </remarks>
public struct Iso8601SerializableDateTimeOffset : IXmlSerializable
{
private DateTimeOffset value;
public Iso8601SerializableDateTimeOffset(DateTimeOffset value)
{
this.value = value;
}
public static implicit operator Iso8601SerializableDateTimeOffset(DateTimeOffset value)
{
return new Iso8601SerializableDateTimeOffset(value);
}
public static implicit operator DateTimeOffset(Iso8601SerializableDateTimeOffset instance)
{
return instance.value;
}
public static bool operator ==(Iso8601SerializableDateTimeOffset a, Iso8601SerializableDateTimeOffset b)
{
return a.value == b.value;
}
public static bool operator !=(Iso8601SerializableDateTimeOffset a, Iso8601SerializableDateTimeOffset b)
{
return a.value != b.value;
}
public static bool operator <(Iso8601SerializableDateTimeOffset a, Iso8601SerializableDateTimeOffset b)
{
return a.value < b.value;
}
public static bool operator >(Iso8601SerializableDateTimeOffset a, Iso8601SerializableDateTimeOffset b)
{
return a.value > b.value;
}
public override bool Equals(object o)
{
if(o is Iso8601SerializableDateTimeOffset)
return value.Equals(((Iso8601SerializableDateTimeOffset)o).value);
else if(o is DateTimeOffset)
return value.Equals((DateTimeOffset)o);
else
return false;
}
public override int GetHashCode()
{
return value.GetHashCode();
}
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
var text = reader.ReadElementString();
value = DateTimeOffset.ParseExact(text, format: "o", formatProvider: null);
}
public override string ToString()
{
return value.ToString(format: "o");
}
public string ToString(string format)
{
return value.ToString(format);
}
public void WriteXml(XmlWriter writer)
{
writer.WriteString(value.ToString(format: "o"));
}
}
Upvotes: 17
Reputation: 51
I'm also unsure of the best way, but here is what I did:
[XmlElement("lastUpdatedTime")]
public string lastUpdatedTimeForXml
{
get { return lastUpdatedTime.ToString(); }
set { lastUpdatedTime = DateTimeOffset.Parse(value); }
}
[XmlIgnore]
public DateTimeOffset lastUpdatedTime;
Upvotes: 5
Reputation: 86947
I've ended up just doing this...
public static double ToUnixEpoch(this DateTimeOffset value)
{
// Create Timespan by subtracting the value provided from
//the Unix Epoch then return the total seconds (which is a UNIX timestamp)
return (double)((value - new DateTime(1970, 1, 1, 0, 0, 0, 0)
.ToLocalTime())).TotalSeconds;
}
public static string ToJsonString(this DateTimeOffset value)
{
return string.Format("\\/Date({0})\\/", value.ToUnixEpoch());
}
[XmlRoot("playersConnected")]
public class PlayersConnectedItem
{
public string name { get; set; }
public string connectedOn { get; set; }
public string server { get; set; }
public string gameType { get; set; }
}
var data = (from q in connectedPlayerLogEntries
select new PlayersConnectedItem
{
name = q.ClientName,
connectedOn = q.CreatedOn.ToJsonString(),
server = q.GameFile.UniqueName,
gameType = q.GameFile.GameType.Description()
});
Done. Not sure if that's the best way .. but now that viewdata property has the same values for either Json or Xml.
Upvotes: 2