C# 强制XML序列化以序列化只读属性

C# 强制XML序列化以序列化只读属性,c#,xml-serialization,C#,Xml Serialization,在C#中,我有一个类,它有一个派生属性,应该通过XML序列化。但是,XML序列化(默认情况下)不会序列化只读属性。我可以通过定义一个空setter来解决这个问题,如下所示: public virtual string IdString { get { return Id.ToString("000000"); } set { /* required for xml serialization */ } } 但是除了编写我自己的ISerializable实现之外,还有一种更简洁、

在C#中,我有一个类,它有一个派生属性,应该通过XML序列化。但是,XML序列化(默认情况下)不会序列化只读属性。我可以通过定义一个空setter来解决这个问题,如下所示:

public virtual string IdString
{
    get { return Id.ToString("000000"); }
    set { /* required for xml serialization */ }
}

但是除了编写我自己的ISerializable实现之外,还有一种更简洁、语义更正确的方法吗?

简言之,没有。使用
XmlSerializer
您可以实现
IXmlSerializable
(这是非常重要的),或者编写基本的DTO(即完全读写)然后将DTO模型转换为主模型

请注意,在某些情况下,
DataContractSerializer
是一个可行的选项,但它不提供对XML的相同控制。但是,使用DCS,您可以执行以下操作:

[DataMember]
public int Id { get; private set; }

老实说,只要有文件证明,这对我来说并不是太糟糕

如果实际调用了setter,则可能会引发异常:

/// <summary>
/// Blah blah blah.
/// </summary>
/// <exception cref="NotSupportedException">Any use of the setter for this property.</exception>
/// <remarks>
/// This property is read only and should not be set.  
/// The setter is provided for XML serialisation.
/// </remarks>
public virtual string IdString
{
    get
    {
        return Id.ToString("000000");
    }
    set
    {
        throw new NotSupportedException("Setting the IdString property is not supported");
    }
}
//
///废话废话。
/// 
///此属性对setter的任何使用。
/// 
///此属性为只读,不应设置。
///setter用于XML序列化。
/// 
公共虚拟字符串IdString
{
得到
{
返回Id.ToString(“000000”);
}
设置
{
抛出新的NotSupportedException(“不支持设置IdString属性”);
}
}

将解决方案再进一步,以允许反序列化也能工作

public class A
{
    private int _id = -1;

    public int Id
    {
        get { return _id; }
        set
        {
            if (_id < 0)
                throw new InvalidOperationException("...");

            if (value < 0)
                throw new ArgumentException("...");

            _id = value;
        }
    }
}
公共A类
{
私有int_id=-1;
公共整数Id
{
获取{return\u id;}
设置
{
如果(_id<0)
抛出新的InvalidOperationException(“…”);
如果(值<0)
抛出新的ArgumentException(“…”);
_id=值;
}
}
}
这将允许将
Id
精确设置为大于或等于0的值一次。任何在之后设置的尝试都将导致
无效操作异常
。这意味着
XmlSerializer
将能够在反序列化期间设置
Id
,但在反序列化之后将永远无法更改。请注意,如果属性是引用类型,则只需检查null即可

如果要序列化/反序列化大量只读属性,这可能不是最佳解决方案,因为这需要大量样板代码。但是,我发现这对于具有1-2个只读属性的类是可以接受的

这仍然是一种攻击,但至少有一点更强大。

对于C#8,允许淘汰
set
,因此您可以这样做:

公共虚拟字符串IdString
{
获取{return Id.ToString(“000000”);}
[过时(“仅用于xml序列化”,错误:true)]
设置{抛出新的NotSupportedException();}
}

如果有人意外使用setter,这将出错。

如果我理解正确,您有一个IdString正在序列化到XML文件,但在反序列化类时被忽略?IdString将始终从Id属性派生,哪个会被序列化和反序列化。在这种情况下,为什么要序列化IdString?@Justin该注释比您的答案要好:)如果状态是最重要的,则不需要序列化属性,因为它是表示状态的字段。但由于XmlSerializer只关心公共定义,而公共定义通常是属性,我希望它能以某种方式处理这种情况。常见的情况是,XML属性旨在为人类可读,或者用于导出到另一个系统。毕竟,XML是一种常见的数据交换格式。这会不会导致反序列化失败,仅仅因为引发了这个异常?@primfaktor是的,但正如我所理解的,这就是问题的关键(它永远不会被反序列化)。如果这不是您想要的行为,您可以在setter中什么也不做,甚至可以解析字符串并设置
Id
属性(可能只有在尚未设置的情况下)。哦,当然可以。写这篇文章时,我的咖啡因摄入量可能很低。
IXmlSerializable
实际上对这个问题非常有效,特别是当你只是序列化而不是反序列化时,这并不难做到。看看这个:第一个
if
是否应该抛出if
\u id
是ge zero,而不是lt zero?实际上,第一个
if
正在阻止任何可能的
设置。正确的实现应该是
If(_idb),但使用此实现时,每次反序列化XML时都会抛出错误