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

All*_*ice 32 c# nullable xml-serialization

我正在序列化这样的类

public MyClass
{
    public int? a { get; set; }
    public int? b { get; set; }
    public int? c { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

所有类型都可以为空,因为我希望在序列化此类对象时存储最少的数据.但是,当它仅使用"a"进行序列化时,我得到以下xml

<MyClass ...>
    <a>3</a>
    <b xsi:nil="true" />
    <c xsi:nil="true" />
</MyClass>
Run Code Online (Sandbox Code Playgroud)

如何将此设置为仅获取非null属性的xml?期望的输出将是

<MyClass ...>
    <a>3</a>
</MyClass>
Run Code Online (Sandbox Code Playgroud)

我想排除这些空值,因为会有几个属性,这是存储在数据库中(是的,这不是我的调用)所以我想保持未使用的数据最小.

All*_*ice 33

您忽略了具有规范的特定元素

public MyClass
{
    public int? a { get; set; }

    [System.Xml.Serialization.XmlIgnore]
    public bool aSpecified { get { return this.a != null; } }

    public int? b { get; set; }
    [System.Xml.Serialization.XmlIgnore]
    public bool bSpecified { get { return this.b != null; } }

    public int? c { get; set; }
    [System.Xml.Serialization.XmlIgnore]
    public bool cSpecified { get { return this.c != null; } }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 注意:我回答这个是因为老实说,我正在寻找一个更好的解决方案,我不是所有这些额外字段的忠实粉丝因为我的班级有几个字段要序列化 (3认同)
  • 我可能误解了你的'奖金',但是如果没有xsi:nil ="true"flotsam,会自动省略空字符串. (2认同)

Mar*_*wis 6

我想你可以创建一个XmlWriter来过滤掉所有带有xsi:nil属性的元素,并将所有其他调用传递给底层真正的writer.

  • @Abacus 你的帖子消失了。伤心。 (2认同)

Ari*_*rie 5

另一个解决方案:正则表达式来拯救,用于\s+<\w+ xsi:nil="true" \/>从包含 XML 的字符串中删除所有空属性。我同意,这不是最优雅的解决方案,只有在您只需要序列化时才有效。但这就是我今天所需要的,我不想{Foo}Specified为所有可以为空的属性添加属性。

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;
}
Run Code Online (Sandbox Code Playgroud)


Big*_*oul 5

很久以前就有人问过这个问题,即使在 2017 年,它似乎仍然非常相关。这里提出的答案都没有让我满意;所以这是我想出的一个简单的解决方案:

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

<\w+\s+\w+:nil="true"(\s+xmlns:\w+="http://www.w3.org/2001/XMLSchema-instance")?\s*/>
Run Code Online (Sandbox Code Playgroud)

这是一个例子:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助。