XmlSerializer默认只序列化public实例字段或属性;private字段、static成员、只读属性等均被跳过,需确保属性有public get和set访问器或使用[XmlElement]等特性控制。

XmlSerializer序列化时字段不输出,为什么?
默认情况下,XmlSerializer 只序列化 public 实例字段或属性。private 字段、static 成员、只读属性(get-only)、自动实现属性但没有 set 访问器,都会被跳过。
常见错误现象:
public class Person { private string _name = "Alice"; public string Name => _name; }——序列化后 XML 中没有 Name 节点。
- 确保属性有 public get 和 set(哪怕空实现):
public string Name { get; set; } - 若必须用只读逻辑,可加
[XmlElement]并配合ShouldSerializeXXX()模式(见下节) - 字段需为 public 才参与序列化;否则改用属性 + 特性标注
如何控制元素名、忽略字段、调整顺序?
靠特性(Attribute)精细控制映射,不是靠命名约定或配置文件。
常用特性及作用:
-
[XmlElement("alias")]:指定 XML 元素名,如把FirstName映射为 -
[XmlAttribute("id")]:将属性序列化为同级元素的 attribute,而非子元素 -
[XmlIgnore]:彻底跳过该成员(比设为 private 更明确、更易维护) -
[XmlElement(Order = 2)]:控制子元素在 XML 中的出现顺序(默认按声明顺序,但显式设 Order 更可靠)
示例:
[XmlRoot("person")]
public class Person
{
[XmlElement("full-name")]
public string FullName { get; set; }
[XmlAttribute("version")]
public int Version { get; set; }
[XmlIgnore]
public bool IsDirty { get; set; }
}
ShouldSerializeXXX() 方法是怎么起作用的?
这是 XmlSerializer 的隐式约定机制:如果存在签名形如 bool ShouldSerialize 的 public 实例方法,它会在序列化前被调用;返回 false 则跳过该属性。
这比 [XmlIgnore] 灵活,因为判断逻辑可依赖其他字段状态。
- 方法名必须严格匹配:如属性叫
BirthDate,方法就得叫ShouldSerializeBirthDate() - 返回类型必须是
bool,无参数,且为 public - 不能带泛型、不能是 static、不能重载
- 它不参与反序列化,仅影响序列化行为
典型场景:只在 HasPhoto == true 时才输出 PhotoData:
public byte[] PhotoData { get; set; }
public bool HasPhoto { get; set; }
public bool ShouldSerializePhotoData() => HasPhoto;
反序列化失败的常见原因和绕过方式
最常见的报错是 InvalidOperationException,提示“未找到无参构造函数”或“无法将字符串转换为 XXX 类型”。这是因为 XmlSerializer 在反序列化时必须调用目标类型的 public 无参构造函数,且所有要赋值的属性/字段必须支持从字符串解析(或有 TypeConverter)。
- 类必须有 public 无参构造函数(即使你写了带参构造,也得额外补一个空的)
- 枚举字段若含未知值,会失败;可用
[XmlEnum("unknown")]标注默认值,或提前预处理 XML - 日期格式必须符合 XSD 标准(如
2023-10-05T14:30:00),否则需自定义TypeConverter或改用DateTimeOffset - 集合类型推荐用
List或数组;IEnumerable或自定义集合类需额外处理(如加[XmlArray]+[XmlArrayItem])
绕过强类型约束的一个办法是先反序列化为 XElement,再手动提取——但这意味着放弃 XmlSerializer 的便利性,转而用 LINQ to XML。










