C# 如何使用 .NET XmlSerializer 使值类型可以为空?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/703137/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 14:04:24  来源:igfitidea点击:

How to make a value type nullable with .NET XmlSerializer?

c#xml-serializationnullvalue-type

提问by Jean-Francois

Let's suppose I have this object:

假设我有这个对象:

[Serializable]
public class MyClass
{
    public int Age { get; set; }
    public int MyClassB { get; set; }
}
[Serializable]
public class MyClassB
{
    public int RandomNumber { get; set; }
}

The XmlSerializer will serialize the object like that:

XmlSerializer 将像这样序列化对象:

<MyClass>
    <Age>0</age>
    <MyClassB>
        <RandomNumber>4234</RandomNumber>
    </MyClassB>
</MyClass>

How can I made the property Age nullable? IE: to not serialize the property Age when it's under 0?

如何使属性 Age 可以为空?IE:当属性年龄小于 0 时不序列化它?

I tried with the Nullable, but it serialize my object like that:

我尝试使用 Nullable,但它会像这样序列化我的对象:

<MyClass>
    <Age d5p1:nil="true" />
    <MyClassB>
        <RandomNumber>4234</RandomNumber>
    </MyClassB>
</MyClass>    

By reading the MSDN documentation I found this:

通过阅读 MSDN 文档,我发现了这一点:

You cannot apply the IsNullable property to a member typed as a value type because a value type cannot contain nullNothingnullptra null reference (Nothing in Visual Basic). Additionally, you cannot set this property to false for nullable value types. When such types are nullNothingnullptra null reference (Nothing in Visual Basic), they will be serialized by setting xsi:nil to true.

不能将 IsNullable 属性应用于键入为值类型的成员,因为值类型不能包含 nullNothingnullptra 空引用(在 Visual Basic 中为 Nothing)。此外,对于可为空的值类型,您不能将此属性设置为 false。当此类类型为 nullNothingnullptra 空引用(在 Visual Basic 中为 Nothing)时,将通过将 xsi:nil 设置为 true 来序列化它们。

source: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.isnullable.aspx

来源:http: //msdn.microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.isnullable.aspx

I understand a value type can't be set to null. A valuetype is always set to something. The serialization can't make the decision to serialize it or not based on it's current value.

我知道不能将值类型设置为 null。值类型总是被设置为某物。序列化无法根据其当前值来决定是否对其进行序列化。

I tried with the attributes, but it didn't work out. I tried creating an agecontainer object and manipulate it's serialization with attributes, but it didn't work out.

我尝试使用属性,但没有成功。我尝试创建一个 agecontainer 对象并使用属性操作它的序列化,但没有成功。

What I really want is:

我真正想要的是:

<MyClass>
    <MyClassB>
        <RandomNumber>4234</RandomNumber>
    </MyClassB>
</MyClass>

When the property Age is below 0 (zero).

当属性年龄低于 0(零)时。



Looks like you'll have to implement custom serialization.

看起来您必须实现自定义序列化。

Yeah, that's what I though too, but I'd like to get away without it.

是的,我也是这么想的,但我想没有它就离开。

In the application, the object is much more complex, and I would like to not handle the serialization myself.

在应用程序中,对象要复杂得多,我不想自己处理序列化。

采纳答案by Samuel

I just discovered this. XmlSerialierlooks for a XXXSpecifiedboolean property to determine if it should be included. This should solve the problem nicely.

我刚刚发现了这一点。XmlSerialier查找XXXSpecified布尔属性以确定是否应包含它。这应该可以很好地解决问题。

[Serializable]
public class MyClass
{
  public int Age { get; set; }
  [XmlIgnore]
  public bool AgeSpecified { get { return Age >= 0; } }
  public int MyClassB { get; set; }
}

[Serializable]
public class MyClassB
{
  public int RandomNumber { get; set; }
}

Proof:

证明:

static string Serialize<T>(T obj)
{
  var serializer = new XmlSerializer(typeof(T));
  var builder = new StringBuilder();
  using (var writer = new StringWriter(builder))
  {
    serializer.Serialize(writer, obj);
    return builder.ToString();
  }
}

static void Main(string[] args)
{
  var withoutAge = new MyClass() { Age = -1 };
  var withAge = new MyClass() { Age = 20 };

  Serialize(withoutAge); // = <MyClass><MyClassB>0</MyClassB></MyClass>
  Serialize(withAge); // = <MyClass><Age>20</Age><MyClassB>0</MyClassB></MyClass>
}


Edit: Yes, it is a documented feature. See the MSDN entry for XmlSerializer

编辑:是的,这是一个记录在案的功能。请参阅MSDN 条目以了解XmlSerializer

Another option is to use a special pattern to create a Boolean field recognized by the XmlSerializer, and to apply the XmlIgnoreAttribute to the field. The pattern is created in the form of propertyNameSpecified. For example, if there is a field named "MyFirstName" you would also create a field named "MyFirstNameSpecified" that instructs the XmlSerializer whether to generate the XML element named "MyFirstName".

另一种选择是使用特殊模式创建 XmlSerializer 识别的布尔字段,并将 XmlIgnoreAttribute 应用于该字段。该模式以propertyNameSpecified 的形式创建。例如,如果有一个名为“MyFirstName”的字段,您还将创建一个名为“MyFirstNameSpecified”的字段,以指示 XmlSerializer 是否生成名为“MyFirstName”的 XML 元素。

回答by Dog Ears

This should help Make Age int?and..

这应该有助于使年龄int?和..

public bool ShouldSerializeAge() { return Age.HasValue; }

..it does mean adding the ShouldSerializeXXX methods to your class!

..它确实意味着将 ShouldSerializeXXX 方法添加到您的类中!

回答by Dаn

You need to do custom XML serialization; see IXmlSerializer.

你需要做自定义的XML序列化;请参阅IXmlSerializer

public class MyClass : IXmlSerializable
{
    public int Age { get; set; }
    public MyClassB MyClassB { get; set; }

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        // http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        if (reader.IsStartElement("Age"))
            Age = reader.ReadContentAsInt();

        var serializer = new XmlSerializer(typeof(MyClassB));
        MyClassB = (MyClassB)serializer.Deserialize(reader);
    }

    public void WriteXml(XmlWriter writer)
    {
        if (Age > 0)
        {
            writer.WriteStartElement("Age");
            writer.WriteValue(Age);
            writer.WriteEndElement();
        }

        var serializer = new XmlSerializer(typeof(MyClassB));
        serializer.Serialize(writer, MyClassB);
    }
}

回答by hoang

Forget about Nullable ... ShouldSerializeXXX is a pretty solution. Here Age will be serialized upon your condition.

忘记 Nullable ...... ShouldSerializeXXX 是一个很好的解决方案。此处年龄将根据您的情况进行连载。

[Serializable]
public class MyClass
{
    public int Age { get; set; }
    public int MyClassB { get; set; }

    #region Conditional Serialization
    public bool ShouldSerializeAge() { return age > 0; }
    #endregion
}

[Serializable]
public class MyClassB
{
    public int RandomNumber { get; set; }
}

回答by jumpalongjim

Extending Samuel's answer and Greg Beech's comment to the case of a boolean property: if the property is of type bool then you can't write a simple test in the propertySpecified property.

将 Samuel 的回答和 Greg Beech 的评论扩展到布尔属性的情况:如果属性是 bool 类型,那么您不能在 propertySpecified 属性中编写简单的测试。

A solution is to use a Nullable<bool> type, then the test in the propertySpecified property is simply property.HasValue. e.g.

一种解决方案是使用 Nullable<bool> 类型,然后在 propertySpecified 属性中的测试只是 property.HasValue。例如

using System.Xml.Serialization;

public class Person
{
    public bool? Employed { get; set; }

    [XmlIgnore]
    public bool EmployedSpecified { get { return Employed.HasValue; } }
}

An alternative to using a nullable type for a numeric property (suggested by Greg Beech) is to set the value property to an invalid default value, such as -1, as follows:

对数字属性使用可为空类型的替代方法(由 Greg Beech 建议)是将 value 属性设置为无效的默认值,例如 -1,如下所示:

using System.ComponentModel;
using System.Xml.Serialization;

public class Person
{
    [DefaultValue(-1)]
    public int Age { get; set; }

    [XmlIgnore]
    public bool AgeSpecified { get { return Age >= 0; } }
}

回答by JustAsItSounds

xsd.exe will autogenerate the XXXSpecified property and accessors if you set the 'minoccurs' attribute as 'minoccurs="0"' for an element ... if you are using a schema to define your xml/class

xsd.exe 将自动生成 XXXSpecified 属性和访问器,如果您将元素的 'minoccurs' 属性设置为 'minoccurs="0"' ... 如果您使用架构来定义您的 xml/class

回答by Yochai Timmer

You can use XmlElementAttribute.IsNullable:

您可以使用XmlElementAttribute.IsNullable

[Serializable]
public class MyClass
{
    [XmlElement(IsNullable = true)]
    public int? Age { get; set; }

    public int MyClassB { get; set; }
}