使用XmlSerializer处理SyncML:Get标签混合元素序列化问题
问题
我正尝试使用XmlSerializer对SyncML进行序列化/反序列化,在处理如下示例中的<Get>标签结构时遇到困难:
<SyncML xmlns="SYNCML:SYNCML1.2"> <SyncHdr> <VerDTD>1.2</VerDTD> <VerProto>DM/1.2</VerProto> <!-- etc. --> </SyncHdr> <SyncBody> <Status> <CmdID>100</CmdID> <MsgRef>1</MsgRef> <CmdRef>0</CmdRef> <!-- etc. --> </Status> <Status> <CmdID>103</CmdID> <MsgRef>1</MsgRef> <CmdRef>4</CmdRef> <!-- etc. --> </Status> <Get> <CmdID>104</CmdID> <Item> <Target> <!-- etc. --> </Target> </Item> <Item> <Target> <!-- etc. --> </Target> </Item> </Get> <Get> <CmdID>105</CmdID> <Item> <Target> <!-- etc. --> </Target> </Item> <Item> <Target> <!-- etc. --> </Target> </Item> </Get> <Sequence> <CmdID>107</CmdID> <Replace> <CmdID>108</CmdID> <Item> <Target> <!-- etc. --> </Target> </Item> <Item> <Target> <!-- etc. --> </Target> </Item> </Replace> <Replace> <CmdID>109</CmdID> <Item> <Target> <!-- etc. --> </Target> </Item> </Replace> <Get> <CmdID>110</CmdID> <Item> <Target> <!-- etc. --> </Target> </Item> </Get> </Sequence> <Final/> </SyncBody> </SyncML>
我目前的类定义如下:
[XmlRoot("SyncML", Namespace = "SYNCML:SYNCML1.2")] public class SyncML { [XmlElement] public SyncHdr SyncHdr { get; set; } [XmlArray("SyncBody")] [XmlArrayItem("Status", Type = typeof(StatusCommand))] [XmlArrayItem("Get", Type = typeof(GetCommand))] public SyncBody SyncBody { get; set; } } public class SyncHdr { [XmlElement("VerDTD")] public string VerDtd { get; set; } [XmlElement("VerProto")] public string VerProto { get; set; } // etc. } public class SyncBody : List<SyncCommand> { } public abstract class SyncCommand : List<Item> { [XmlElement("CmdID")] public int CmdId { get; set; } } public class StatusCommand : SyncCommand { [XmlElement("MsgRef")] public int MsgRef { get; set; } [XmlElement("CmdRef")] public int CmdRef { get; set; } // etc. } public class GetCommand : SyncCommand { public List<Item> Items { get; set; } } public class Item { [XmlElement("Target")] public Location Target { get; set; } } public class Location { [XmlElement("LocURI")] public string LocUri { get; set; } }
问题在于XML中的<Get>标签包含一个CmdID子元素(与<Status>元素类似),同时还有任意数量的Item元素。是否可以通过特性配置GetCommand类来处理这种情况?还是需要调整我的模型结构?
解决方案
核心问题出在让SyncCommand继承自List<Item>,XmlSerializer对集合类的序列化逻辑会优先处理集合项,导致CmdID这类普通属性被当作XML元素的属性而非子元素,不符合SyncML的结构要求。需要调整模型结构,具体修改如下:
1. 重构SyncCommand基类
去掉集合继承,改为添加List<Item>属性并标记[XmlElement("Item")],确保Item直接成为父命令元素的子元素:
public abstract class SyncCommand { [XmlElement("CmdID")] public int CmdId { get; set; } [XmlElement("Item")] public List<Item> Items { get; set; } = new List<Item>(); }
2. 调整StatusCommand
<Status>元素不包含Item子元素,所以忽略基类的Items属性:
public class StatusCommand : SyncCommand { [XmlElement("MsgRef")] public int MsgRef { get; set; } [XmlElement("CmdRef")] public int CmdRef { get; set; } // 忽略Items属性,避免序列化空元素 [XmlIgnore] public override List<Item> Items { get => base.Items; set => base.Items = value; } }
3. 简化GetCommand
直接使用基类的Items属性,无需额外定义:
public class GetCommand : SyncCommand { // 继承基类的CmdId和Items属性即可 }
4. 修正SyncML类的SyncBody配置
补充XML中存在的<Sequence>、<Final>等命令的类型映射,确保所有元素都能被处理:
[XmlRoot("SyncML", Namespace = "SYNCML:SYNCML1.2")] public class SyncML { [XmlElement("SyncHdr")] public SyncHdr SyncHdr { get; set; } [XmlArray("SyncBody")] [XmlArrayItem("Status", Type = typeof(StatusCommand))] [XmlArrayItem("Get", Type = typeof(GetCommand))] [XmlArrayItem("Sequence", Type = typeof(SequenceCommand))] [XmlArrayItem("Final", Type = typeof(FinalCommand))] public List<SyncCommand> SyncBody { get; set; } = new List<SyncCommand>(); } // 若仍需保留SyncBody类,可改为: // public class SyncBody : List<SyncCommand> { } // 然后将SyncML的SyncBody属性类型改为SyncBody即可
5. 补充缺失的命令类
XML中的<Sequence>、<Replace>、<Final>需要对应类来处理:
// 处理<Sequence>元素,包含子命令集合 public class SequenceCommand : SyncCommand { [XmlElement("Replace", Type = typeof(ReplaceCommand))] [XmlElement("Get", Type = typeof(GetCommand))] public List<SyncCommand> SubCommands { get; set; } = new List<SyncCommand>(); // Sequence无直接Item子元素,忽略基类Items [XmlIgnore] public override List<Item> Items { get => base.Items; set => base.Items = value; } } // 处理<Replace>元素,结构与Get类似 public class ReplaceCommand : SyncCommand { } // 处理<Final>空元素 public class FinalCommand : SyncCommand { [XmlIgnore] public override List<Item> Items { get => base.Items; set => base.Items = value; } [XmlEmptyElement] public bool Final { get; set; } = true; }
关键说明
- 避免命令类继承集合:
XmlSerializer对集合类的序列化逻辑特殊,会导致普通属性无法正确生成为子元素。 - 用
XmlElement指定子元素:[XmlElement("Item")]确保每个Item直接作为父元素的子节点,而非被包裹在额外的容器元素中。 - 覆盖并忽略无关属性:对于不需要
Item的命令(如Status、Sequence),通过[XmlIgnore]避免序列化空的Item元素。
内容的提问来源于stack exchange,提问作者Zenilogix
相关产品推荐
相关产品推荐

