C# XmlSerializer - 处理无效 AllXsd 值的通用自定义适配器(如 java 的 XmlJavaTypeAdapter)

C# XmlSerializer - Generic custom adapter to handle not valid AllXsd value (like java's XmlJavaTypeAdapter)

我使用大量可序列化对象反序列化一个 xml。 我已经读到,为了避免 DateTime 中的无效 AllXsd 值,我必须创建一个辅助字符串 属性 它将输入作为字符串处理并将其转换。 即

[XmlIgnore]
public DateTime? DateUpdated { get; set; }

[XmlElement("updateDate")]
public string DateUpdatedAsText
{
    set
    {
        if (!string.IsNullOrWhiteSpace(value))
        {
            try
            {
                DateUpdated = DateTime.Parse(value, CultureInfo.InvariantCulture);
            }
            catch (Exception) { }
        }
    }
    get
    {
        return DateUpdated.HasValue ? DateUpdated.Value.ToString("yyyy-MM-ddTHH:mm:ss") : null;
    }
}

这已经过测试并且效果很好。然而...

我有超过 100 个包含 DateTime 字段的实体,其中一些实体不止一个,这个解决方案不是很实用。我将不得不在所有这些中实施这一点,如果将来我想改变任何东西,我将不得不在所有这些中再次实施。 我如何声明一个通用的自定义适配器来处理所有 DateTime 类型。在 Java 我可以这样做:

@XmlJavaTypeAdapter(CalendarXmlAdapter.class)
@Column(name = "updateDate")
private Calendar DateUpdated;

并在 CalendarXmlAdapter.class 中指定编组和解组

是否有针对 C# 的类似解决方案System.Xml.Serialization.XmlSerializer?

提前致谢

已编辑

解决方案: 它是@dbc 评论和@steve16351 答案的组合。我使用了 CustomDateTime class(在 ReadXml 中做了一些小改动,但不重要),在 SomeDate 字段的声明中(仍然是 DateTime?类型)我这样使用它

    [XmlElement(ElementName = "updateDate", IsNullable = true, Type = typeof(CustomDateTime))]
    public DateTime? DateUpdated { get; set; }

转换顺利进行

如果您需要对反序列化进行更多控制,可以使用 IXmlSerializable。据我所知,虽然您不能全局为 XmlSerializer 提供特定类型的自定义转换器,但您可以编写代理 DateTime class,如下所示实现 IXmlSerializable,在我的view 比字符串 属性 和相应的 DateTime 属性 的解决方案更优雅;并且会减少对代码库的破坏。

这是此类解决方案的示例。它还利用隐式运算符来避免在您自己的代码中转换 to/from DateTime? 的需要。

class Program
{
    static void Main(string[] args)
    {
        XmlSerializer s = new XmlSerializer(typeof(MyClass));
        MyClass myClass = null;
        using (var sr = new StringReader(@"<myXml><updateDate>20181008</updateDate><someProp>Hello, world</someProp></myXml>"))            
            myClass = s.Deserialize(sr) as MyClass;            

        DateTime? myValue = myClass.SomeDate;
        Console.WriteLine($"{myClass.SomeDate}");
        Console.ReadKey();
    }
}

[XmlRoot("myXml")]
public class MyClass
{
    [XmlElement("updateDate")]
    public CustomDateTime SomeDate { get; set; }
    [XmlElement("someProp")]
    public string SomeProp { get; set; }

}

public class CustomDateTime : IXmlSerializable
{
    public DateTime? _dateTime { get; set; }
    private const string EXPECTED_FORMAT = "yyyyMMdd";

    public XmlSchema GetSchema()
    {
        throw new NotImplementedException();
    }

    public void ReadXml(XmlReader reader)
    {
        var elementContent = reader.ReadElementContentAsString();
        _dateTime = String.IsNullOrWhiteSpace(elementContent) ? (DateTime?)null : DateTime.ParseExact(elementContent, EXPECTED_FORMAT, CultureInfo.InvariantCulture);
    }

    public void WriteXml(XmlWriter writer)
    {
        if (!_dateTime.HasValue) return;
        writer.WriteString(_dateTime.Value.ToString(EXPECTED_FORMAT));
    }

    public static implicit operator DateTime? (CustomDateTime input)
    {
        return input._dateTime;
    }

    public static implicit operator CustomDateTime (DateTime input)
    {
        return new CustomDateTime() { _dateTime = input };
    }

    public override string ToString()
    {
        if (_dateTime == null) return null;
        return _dateTime.Value.ToString();
    }
}