XML序列化 - 隐藏空值

158

在使用标准的.NET Xml序列化器时,是否有任何方法可以隐藏所有空值? 以下是我的类输出的示例。 如果可空整数设置为null,则不希望输出它们。

当前Xml输出:

<?xml version="1.0" encoding="utf-8"?>
<myClass>
   <myNullableInt p2:nil="true" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance" />
   <myOtherInt>-1</myOtherInt>
</myClass>

我想要的是:

<?xml version="1.0" encoding="utf-8"?>
<myClass>
   <myOtherInt>-1</myOtherInt>
</myClass>
7个回答

295

你可以使用模式ShouldSerialize{PropertyName}创建一个函数,该函数告诉XmlSerializer是否应序列化成员。

例如,如果您的类属性名为MyNullableInt,您可以这样做:

public bool ShouldSerializeMyNullableInt() 
{
  return MyNullableInt.HasValue;
}

这是一个完整的示例

public class Person
{
  public string Name {get;set;}
  public int? Age {get;set;}
  public bool ShouldSerializeAge()
  {
    return Age.HasValue;
  }
}

使用以下代码序列化

Person thePerson = new Person(){Name="Chris"};
XmlSerializer xs = new XmlSerializer(typeof(Person));
StringWriter sw = new StringWriter();
xs.Serialize(sw, thePerson);

以下是XML的结果 - 请注意没有年龄信息

<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Chris</Name>
</Person>

14
一个词:棒极了!MSDN ShouldSerialize - scheien
11
如果属性没有被标记为XmlAttribute-attribute,那么ShouldSerialize模式才能起作用(我认为这应该行得通,因为属性可以是可选的,但事实并非如此)。 - Matze
@ChrisTaylor 是的;我也是这么认为的。棘手的问题在于,当我从可空 int 属性中移除 XmlAttribute 后,XmlSerializer 实例的创建失败(由于反射类型时出现错误)。 - Matze
如果所有属性都为null,有什么办法可以删除XML根元素吗? - neel shah
2
@PierredeLESPINAY - 从Visual Studio 2015及以上版本开始,您可以使用以下代码:public bool ShouldSerializeAge() => Age.HasValue; - RooiWillie
显示剩余12条评论

40

除了Chris Taylor所写的内容之外:如果您有将某些内容序列化为属性,您可以在类中命名为{PropertyName}Specified的属性来控制是否应进行序列化。代码示例如下:

public class MyClass
{
    [XmlAttribute]
    public int MyValue;

    [XmlIgnore]
    public bool MyValueSpecified;
}

请注意,{PropertyName}Specified 属性必须是布尔类型。 - sinsedrix
1
也可以作为函数使用。例如,如果 MyValueint?,则可以这样做 public bool MyValueSpecified => MyValue.HasValue; - OfirD
2
@OfirD public bool MyValueSpecified => MyValue.HasValue; 不是函数,而是只读属性。与此相同的是:public bool MyValueSpecified { get { return MyValue.HasValue; } } - Gh61

33

存在一个名为XmlElementAttribute.IsNullable的属性。

如果将IsNullable属性设置为true,则对于被设置为null引用的类成员,将生成xsi:nil属性。

以下示例显示应用了XmlElementAttribute的字段,并将IsNullable属性设置为false。

public class MyClass
{
   [XmlElement(IsNullable = false)]
   public string Group;
}

你可以查看其他的XmlElementAttribute用于更改序列化中的名称等。


15
不幸的是,这只适用于引用类型,而不适用于值类型或其可空类型的对应物。 - Vincent Sels
4
@VincentSels是正确的。MSDN说:由于值类型不能包含null,因此无法将IsNullable属性应用于作为值类型的成员。此外,对于可空值类型,您也不能将此属性设置为false。当这些类型为null时,将通过将xsi:nil设置为true来对它们进行序列化。 - bouvierr

15

您可以定义一些默认值,这样可以防止字段被序列化。

    [XmlElement, DefaultValue("")]
    string data;

    [XmlArray, DefaultValue(null)]
    List<string> data;

1
很遗憾,这对可空值类型不起作用。 - bubi

4

我更喜欢自己创建XML,而不使用自动生成的标签。这样我就可以忽略创建空值节点:

public static string ConvertToXML<T>(T objectToConvert)
    {
        XmlDocument doc = new XmlDocument();
        XmlNode root = doc.CreateNode(XmlNodeType.Element, objectToConvert.GetType().Name, string.Empty);
        doc.AppendChild(root);
        XmlNode childNode;

        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
        foreach (PropertyDescriptor prop in properties)
        {
            if (prop.GetValue(objectToConvert) != null)
            {
                childNode = doc.CreateNode(XmlNodeType.Element, prop.Name, string.Empty);
                childNode.InnerText = prop.GetValue(objectToConvert).ToString();
                root.AppendChild(childNode);
            }
        }            

        return doc.OuterXml;
    }

1
在我的情况下,可为空的变量/元素都是字符串类型。因此,我简单地进行了检查,并在NULL的情况下将它们赋值为string.Empty。这样我就摆脱了不必要的nil和xmlns属性(p3:nil="true" xmlns:p3="http://www.w3.org/2001/XMLSchema-instance)。
// Example:

myNullableStringElement = varCarryingValue ?? string.Empty

// OR

myNullableStringElement = myNullableStringElement ?? string.Empty

1
这个解决方案非常有限,只适用于字符串。对于其他类型,空字符串仍然是一个值。一些解析器尝试查找属性,如果找到则尝试将该值转换为目标类型。对于这样的解析器,缺少属性意味着空值,如果存在属性,则必须具有有效值。 - ZafarYousafi

0
private static string ToXml(Person obj)
{
  XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
  namespaces.Add(string.Empty, string.Empty);

  string retval = null;
  if (obj != null)
  {
    StringBuilder sb = new StringBuilder();
    using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { OmitXmlDeclaration = true }))
    {
      new XmlSerializer(obj.GetType()).Serialize(writer, obj,namespaces);
    }
    retval = sb.ToString();
  }
  return retval;
}

这只是移除了根上的规范,但是对于可空引用类型上的每个空值,你会得到额外的规范。 - undefined

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