如何在序列化时忽略空或空值的可为空属性?

5

我有一个用于 Xml 序列化的类。

在其中,我有一个可空属性,该属性带有 XmlAttribute 标记:

 [XmlAttribute("lastUpdated")]
 public DateTime? LastUpdated { get; set; }

如何在属性为null或空时从序列化中忽略它?
我尝试了下面的方法,但当有值时它不会被序列化(总是被忽略):
  [XmlIgnore]
        public DateTime? LastUpdatedValue { get; set; }

        [XmlAttribute("lastUpdated")]
       public DateTime LastUpdated { get; set; }

        public bool ShouldSerializeLastUpdated()
        {
            return LastUpdatedValue.HasValue;
        }

5
你看过这个吗?https://dev59.com/D3VC5IYBdhLWcg3wlyQo?rq=1 - Yahya
你期望XML的样子是什么?在这种情况下,如果属性为空,你将得到<lastUpdated xsi:nil="true"/>;你可以事后处理你的xml(更容易),或者你可以编写一个XmlWriter类(更难,但性能更好)。还有其他选项,既困难又臃肿你的代码。 - Sten Petrov
不,我只想在XmlAttribute为空时忽略它。但DateTime?是不可序列化的。如何替换它? - The Light
5个回答

11

在XmlSerialization中不直接支持Nullable。

如果您想使用可空属性,必须使用非可空属性,并添加一个与属性名称相同且带有后缀"Specified"的布尔属性,用于指定何时可以序列化该属性。

以下是一个示例:

    private DateTime? _lastUpdated;

    [XmlAttribute("lastUpdated")]
    public DateTime LastUpdated {
        get {
            return (DateTime)_lastUpdated;
        }
        set
        {
            _lastUpdated = value;
        }
    }

    public bool LastUpdatedSpecified
    {
        get
        {
            return _lastUpdated.HasValue;
        }
    }

谢谢,但问题是我无法再使用XmlSerializer序列化它。我得到了这个错误:+ InnerException {"反映属性'LastUpdated'时出错。"} System.Exception {System.InvalidOperationException} - The Light
抱歉,我太快了,我会修正并编辑我的代码。您能试一下吗? - binard
@TheLight,你能否把我的答案选为最佳回复? - binard
如果_lastUpdated没有值,return (DateTime)_lastUpdated;会抛出InvalidOperationException异常。 - isalgueiro
LastUpdatedSpecified 用在哪里? - rajibdotnet

6

我知道这个话题很老了。这是我提出的解决方案。一个封装类型并具有隐式转型到该类型的类。当序列化时,成员变量可以标记为IsNullable = false,而不会出现编译器错误,并阻止其在为空时被序列化。

public class Optional<T> where T : struct, IComparable
{
    public Optional(T valueObject)
    {
        Value = valueObject;
    }

    public Optional()
    {
    }

    [XmlText]
    public T Value { get; set; }

    public static implicit operator T(Optional<T> objectToCast)
    {
        return objectToCast.Value;
    }

    public static implicit operator Optional<T>(T objectToCast)
    {
        return new Optional<T>(objectToCast);
    }
}

然后在你的类中使用它。
[Serializable]
[XmlRoot(ElementName = "foo")]
public class foo
{
   [XmlElement(ElementName = "myInteger", isNullable = false)]
   Optional<int> myInt;
}

你可以做类似以下的事情:
        myFoo.myInt = 7;
        int j = 8 + myFoo.myInt;

在所有情况下,它都是一个整数。但为了序列化的目的,它可以为 null,并且被阻止进行序列化。


1
谢谢,这对我很有效!当我将其与另一种自定义数据类型一起使用时(我的布尔值需要序列化为Y / N),我已将IComparable更改为IComparable <T>,在其中我可以在我的CustomBool类型中实现IComparable <CustomBool>。 - BerDev
这对于[XmlElement(DataType="date")]无效。'date'是XmlElementAttribute.DataType属性的无效值。该属性只能用于原始类型。 - JJS

4

这对我来说有效。

    [XmlIgnore]
    public float? Speed { get; set; }

    [XmlAttribute("Speed")]
    public float SpeedSerializable
    {
        get
        {
            return this.Speed.Value;
        }
        set { this.Speed = value; }
    }

    public bool ShouldSerializeSpeedSerializable()
    {
          return Speed.HasValue;
    }

2
注意:这在WCF中的SOAP合同中肯定有效。我没有测试其他Xml序列化场景。
当使用[DataContract]时,您可以使用 [DataMember(EmitDefaultValue = false)]
  • 对于布尔值,只有在其为true时才会发出该值。
  • 对于可空布尔值,仅当其不为 null 时才会发出。
  • 对于字符串,只有在其不为 null 时才会发出该值。
  • 对于int,只有在其不为 0 时才会发出该值。
等等。
请确保将[DataContract]放在类本身上,在您要序列化的所有成员上放置[DataMember],无论是否指定了EmitDefaultValue

将EmitDefaultValue属性设置为false不是推荐的做法。只有在特定需要(例如为了互操作性或减小数据大小)时才应该这样做。

https://msdn.microsoft.com/zh-cn/library/system.runtime.serialization.datamemberattribute.emitdefaultvalue(v=vs.110).aspx

1
您可以使用 XmlElementAttribute.IsNullable 属性来实现:
[Serializable]
public class Result
{
    [XmlElement(IsNullable = true)]
    public DateTime? LastUpdated  { get; set; }
}

它不能用于属性。只能用于元素。 - Belurd
这个做的是相反于所要求的。从文档中可以看到:"获取或设置一个值,该值指示XmlSerializer是否必须将设置为null的成员序列化为一个带有xsi:nil属性设置为true的空标记。" - Simon Morgan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接