当数据表示为Xml时,此类中的DateTimeOffset
属性不会被呈现。我需要做些什么才能告诉Xml序列化将其正确呈现为DateTime
或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; }
}
和一些样本数据......
<?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>
我希望有可能通过属性我可以在属性上装饰......
有什么方法可以摆脱根节点中声明的那两个命名空间?我应该吗?
答案 0 :(得分:25)
这已经晚了几年,但是这是使用ISO 8601 完全序列化DateTimeOffset
的快捷方式:
[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;
答案 1 :(得分:14)
我想出了这个结构,它实现了基于ISO 8601格式的XML序列化(例如2011-11-11T15:05:46.4733406+01:00
)。提示:尝试解析诸如DateTime
之类的2011-11-11T15:05:46
值会按预期失败。
欢迎反馈。我没有在这里包含单元测试,因为那文本太多了。
/// <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"));
}
}
答案 2 :(得分:5)
我也不确定最好的方法,但这就是我所做的:
[XmlElement("lastUpdatedTime")]
public string lastUpdatedTimeForXml
{
get { return lastUpdatedTime.ToString(); }
set { lastUpdatedTime = DateTimeOffset.Parse(value); }
}
[XmlIgnore]
public DateTimeOffset lastUpdatedTime;
答案 3 :(得分:4)
我在这里找到了解决方案:http://tneustaedter.blogspot.com/2012/02/proper-way-to-serialize-and-deserialize.html
用DataContractSerializer替换XmlSerializer非常棒。 请参阅以下示例代码:
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);
}
}
答案 4 :(得分:2)
解决此问题的一种方法是让您的类实现接口IXmlSerializable
。
实现此界面会强制序列化程序调用“覆盖的”WriteXml
和ReadXml
方法。
类似的东西:
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...
}
当你读到它时:
DateTimeOffset offset;
if(DateTimeoffset.TryParse(reader.Value, out offset))
{
connectedOn = offset;
}
这是一个麻烦,但我不能用任何其他方式。 此解决方案还可让您完全控制序列化过程(这是好处)
如果你喜欢这个解决方案,并想要完整的解决方案请注释,我会写下来
关于命名空间 - 我认为你不能摆脱它(我不会得到奖励分数)。
答案 5 :(得分:2)
我最终只是这样做......
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()
});
完成。不确定这是否是最佳方式..但现在viewdata属性对于Json或Xml具有相同的值。
答案 6 :(得分:0)
要补充@Peter的答案,如果您使用的是ADO.NET实体模型(.edmx),因此所有访问修饰符都是在局部类中自动生成的,则可以编辑模板(MyDB.tt)来制作它使用internal
修饰符生成DateTimeOffset类型。只需将Property()
方法替换为下面的方法即可。
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)));
}