C# 如何在使用XmlSerializer时排除null属性

C# 如何在使用XmlSerializer时排除null属性,c#,xml-serialization,nullable,C#,Xml Serialization,Nullable,我正在序列化这样一个类 public MyClass { public int? a { get; set; } public int? b { get; set; } public int? c { get; set; } } 所有类型都可以为null,因为我希望在序列化此类型的对象时存储最少的数据。但是,当它被序列化时,只填充了“a”,我得到以下xml <MyClass ...> <a>3</a> <b xs

我正在序列化这样一个类

public MyClass
{
    public int? a { get; set; }
    public int? b { get; set; }
    public int? c { get; set; }
}
所有类型都可以为null,因为我希望在序列化此类型的对象时存储最少的数据。但是,当它被序列化时,只填充了“a”,我得到以下xml

<MyClass ...>
    <a>3</a>
    <b xsi:nil="true" />
    <c xsi:nil="true" />
</MyClass>

3.
如何设置为仅获取非空属性的xml?期望的输出是

<MyClass ...>
    <a>3</a>
</MyClass>

3.

我想排除这些空值,因为将有多个属性,并且这些属性存储在数据库中(是的,这不是我的调用),所以我希望将未使用的数据保持在最小值。

您可以忽略具有


{field}指定的属性将通过返回true/false告诉序列化程序是否应该序列化相应的字段。

我想您可以创建一个XmlWriter,用xsi:nil属性过滤掉所有元素,并将所有其他调用传递给底层的true writer。

编写这样的代码的最简单方法是,精确输出非常重要:

  • 编写一个XML模式,描述您所需的确切格式
  • 使用
    xsd.exe
    将架构转换为类
  • 将类转换回模式(再次使用
    xsd.exe
    ),并对照原始模式进行检查,以确保序列化程序正确地再现了所需的所有行为
  • 调整和重复,直到你有工作代码

    如果您不确定最初使用的确切数据类型,请从步骤3开始,而不是从步骤1开始,然后调整

    IIRC,对于您的示例,您几乎肯定会得到您已经描述过的
    指定的
    属性,但为您生成这些属性肯定比手工编写要好。:-)

    将元素标记为 [XmlElement(“elementName”,IsNullable=false)]
    空值将被忽略。

    迟做总比不做好

    我找到了一种方法(可能只适用于我不知道的最新框架)。 我正在为WCF webservice合约使用DataMember属性,并将我的对象标记为:

    [DataMember(EmitDefaultValue = false)]
    public decimal? RentPrice { get; set; }
    

    还有另一种解决方案:为了拯救regex,请使用
    \s+
    从包含XML的字符串中删除所有空属性。 我同意,这不是最优雅的解决方案,只有在您只需要序列化的情况下才有效。但这就是我今天所需要的,我不想为所有可为null的属性添加
    {Foo}指定的
    属性

    public string ToXml()
    {
        string result;
    
        var serializer = new XmlSerializer(this.GetType());
    
        using (var writer = new StringWriter())
        {
            serializer.Serialize(writer, this);
            result = writer.ToString();
        }
    
        serializer = null;
    
        // Replace all nullable fields, other solution would be to use add PropSpecified property for all properties that are not strings
        result = Regex.Replace(result, "\\s+<\\w+ xsi:nil=\"true\" \\/>", string.Empty);
    
        return result;
    }
    
    公共字符串ToXml()
    {
    字符串结果;
    var serializer=新的XmlSerializer(this.GetType());
    使用(var writer=new StringWriter())
    {
    serializer.Serialize(writer,this);
    结果=writer.ToString();
    }
    序列化程序=null;
    //替换所有可为null的字段,另一种解决方案是对所有非字符串的属性使用addpropspecified属性
    结果=Regex.Replace(结果“\\s+”,string.Empty);
    返回结果;
    }
    
    1)扩展

     public static string Serialize<T>(this T value) {
            if (value == null) {
                return string.Empty;
            }
            try {
                var xmlserializer = new XmlSerializer(typeof(T));
                var stringWriter = new Utf8StringWriter();
                using (var writer = XmlWriter.Create(stringWriter)) {
                    xmlserializer.Serialize(writer, value);
                    return stringWriter.ToString();
                }
            } catch (Exception ex) {
                throw new Exception("An error occurred", ex);
            }
        }
    
    2) 创建XElement

    XElement xml = XElement.Parse(objectToSerialization.Serialize());
    
    3) 删除零的

    xml.Descendants().Where(x => x.Value.IsNullOrEmpty() && x.Attributes().Where(y => y.Name.LocalName == "nil" && y.Value == "true").Count() > 0).Remove();
    
    4) 保存到文件

    xml.Save(xmlFilePath);
    

    这个问题很久以前就被提出了,但即使在2017年也仍然非常相关。这里提出的答案没有一个让我不满意,因此我提出了一个简单的解决方案:

    使用正则表达式是关键。因为我们对XmlSerializer的行为没有太多的控制,所以我们不要试图阻止它序列化那些可为null的值类型。相反,只需获取序列化输出,并使用正则表达式将不需要的元素替换为空字符串。使用的模式(在C#中)是:

    
    
    下面是一个例子:

    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Xml;
    using System.Xml.Serialization;
    
    namespace MyNamespace
    {
        /// <summary>
        /// Provides extension methods for XML-related operations.
        /// </summary>
        public static class XmlSerializerExtension
        {
            /// <summary>
            /// Serializes the specified object and returns the XML document as a string.
            /// </summary>
            /// <param name="obj">The object to serialize.</param>
            /// <param name="namespaces">The <see cref="XmlSerializerNamespaces"/> referenced by the object.</param>
            /// <returns>An XML string that represents the serialized object.</returns>
            public static string Serialize(this object obj, XmlSerializerNamespaces namespaces = null)
            {
                var xser = new XmlSerializer(obj.GetType());
                var sb = new StringBuilder();
    
                using (var sw = new StringWriter(sb))
                {
                    using (var xtw = new XmlTextWriter(sw))
                    {
                        if (namespaces == null)
                            xser.Serialize(xtw, obj);
                        else
                            xser.Serialize(xtw, obj, namespaces);
                    }
                }
    
                return sb.ToString().StripNullableEmptyXmlElements();
            }
    
            /// <summary>
            /// Removes all empty XML elements that are marked with the nil="true" attribute.
            /// </summary>
            /// <param name="input">The input for which to replace the content.    </param>
            /// <param name="compactOutput">true to make the output more compact, if indentation was used; otherwise, false.</param>
            /// <returns>A cleansed string.</returns>
            public static string StripNullableEmptyXmlElements(this string input, bool compactOutput = false)
            {
                const RegexOptions OPTIONS =
                RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline;
    
                var result = Regex.Replace(
                    input,
                    @"<\w+\s+\w+:nil=""true""(\s+xmlns:\w+=""http://www.w3.org/2001/XMLSchema-instance"")?\s*/>",
                    string.Empty,
                    OPTIONS
                );
    
                if (compactOutput)
                {
                    var sb = new StringBuilder();
    
                    using (var sr = new StringReader(result))
                    {
                        string ln;
    
                        while ((ln = sr.ReadLine()) != null)
                        {
                            if (!string.IsNullOrWhiteSpace(ln))
                            {
                                sb.AppendLine(ln);
                            }
                        }
                    }
    
                    result = sb.ToString();
                }
    
                return result;
            }
        }
    }
    
    使用System.IO;
    使用系统文本;
    使用System.Text.RegularExpressions;
    使用System.Xml;
    使用System.Xml.Serialization;
    名称空间MyNamespace
    {
    /// 
    ///为与XML相关的操作提供扩展方法。
    /// 
    公共静态类XmlSerialized
    {
    /// 
    ///序列化指定的对象并以字符串形式返回XML文档。
    /// 
    ///要序列化的对象。
    ///对象引用的对象。
    ///表示序列化对象的XML字符串。
    公共静态字符串序列化(此对象obj,XmlSerializerNamespaces=null)
    {
    var xser=新的XmlSerializer(obj.GetType());
    var sb=新的StringBuilder();
    使用(var sw=新的StringWriter(sb))
    {
    使用(var xtw=新的XmlTextWriter(sw))
    {
    if(名称空间==null)
    序列化(xtw,obj);
    其他的
    序列化(xtw、obj、名称空间);
    }
    }
    返回某人ToString().StripNullableEmptyXmlElements();
    }
    /// 
    ///删除所有标记为nil=“true”属性的空XML元素。
    /// 
    ///要替换其内容的输入。
    ///如果使用缩进,则为true以使输出更紧凑;否则为false。
    ///干净的绳子。
    公共静态字符串stripNullableEmptyXMlements(此字符串输入,bool compactOutput=false)
    {
    常量RegexOptions选项=
    RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline;
    var result=Regex.Replace(
    输入,
    @"",
    字符串。空,
    选择权
    );
    if(压缩输出)
    {
    var sb=新的StringBuilder();
    使用(var sr=新的StringReader(结果))
    {
    字符串ln;
    而((ln=sr.ReadLine())!=null)
    {
    如果(!string.IsNullOrWhiteSpace(ln))
    {
    b.附肢线(ln);
    }
    }
    
    xml.Save(xmlFilePath);
    
    <\w+\s+\w+:nil="true"(\s+xmlns:\w+="http://www.w3.org/2001/XMLSchema-instance")?\s*/>
    
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Xml;
    using System.Xml.Serialization;
    
    namespace MyNamespace
    {
        /// <summary>
        /// Provides extension methods for XML-related operations.
        /// </summary>
        public static class XmlSerializerExtension
        {
            /// <summary>
            /// Serializes the specified object and returns the XML document as a string.
            /// </summary>
            /// <param name="obj">The object to serialize.</param>
            /// <param name="namespaces">The <see cref="XmlSerializerNamespaces"/> referenced by the object.</param>
            /// <returns>An XML string that represents the serialized object.</returns>
            public static string Serialize(this object obj, XmlSerializerNamespaces namespaces = null)
            {
                var xser = new XmlSerializer(obj.GetType());
                var sb = new StringBuilder();
    
                using (var sw = new StringWriter(sb))
                {
                    using (var xtw = new XmlTextWriter(sw))
                    {
                        if (namespaces == null)
                            xser.Serialize(xtw, obj);
                        else
                            xser.Serialize(xtw, obj, namespaces);
                    }
                }
    
                return sb.ToString().StripNullableEmptyXmlElements();
            }
    
            /// <summary>
            /// Removes all empty XML elements that are marked with the nil="true" attribute.
            /// </summary>
            /// <param name="input">The input for which to replace the content.    </param>
            /// <param name="compactOutput">true to make the output more compact, if indentation was used; otherwise, false.</param>
            /// <returns>A cleansed string.</returns>
            public static string StripNullableEmptyXmlElements(this string input, bool compactOutput = false)
            {
                const RegexOptions OPTIONS =
                RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline;
    
                var result = Regex.Replace(
                    input,
                    @"<\w+\s+\w+:nil=""true""(\s+xmlns:\w+=""http://www.w3.org/2001/XMLSchema-instance"")?\s*/>",
                    string.Empty,
                    OPTIONS
                );
    
                if (compactOutput)
                {
                    var sb = new StringBuilder();
    
                    using (var sr = new StringReader(result))
                    {
                        string ln;
    
                        while ((ln = sr.ReadLine()) != null)
                        {
                            if (!string.IsNullOrWhiteSpace(ln))
                            {
                                sb.AppendLine(ln);
                            }
                        }
                    }
    
                    result = sb.ToString();
                }
    
                return result;
            }
        }
    }
    
        public void WriteXml(XmlWriter writer)
        {
            foreach (var p in GetType().GetProperties())
            {
                if (p.GetCustomAttributes(typeof(XmlIgnoreAttribute), false).Any())
                    continue;
    
                var value = p.GetValue(this, null);
    
                if (value != null)
                {
                    writer.WriteStartElement(p.Name);
                    writer.WriteValue(value);
                    writer.WriteEndElement();
                }
            }
        }