.NET序列化排序

我试图使用XmlSerializer和inheritance序列化一些对象,但我在排序结果时遇到一些问题。

下面是一个类似于我设置的例子:〜

public class SerializableBase { [XmlElement(Order = 1)] public bool Property1 { get; set;} [XmlElement(Order = 3)] public bool Property3 { get; set;} } [XmlRoot("Object")] public class SerializableObject1 : SerializableBase { } [XmlRoot("Object")] public class SerializableObject2 : SerializableBase { [XmlElement(Order = 2)] public bool Property2 { get; set;} } 

我想要的结果如下:〜

      

但是我得到的结果是:〜

      

有谁知道它是否可能或任何替代方案?

谢谢

从技术上讲,从纯xml的角度来看,我会说这可能是一件坏事。

.NET隐藏了XmlSerialization之类的复杂function – 在这种情况下,它隐藏了序列化xml应符合的模式。

推断的模式将使用序列元素来描述基类型和扩展类型。 这需要严格的排序 – 即使反序列化器不那么严格并且接受乱序元素。

在xml架构中,在定义扩展类型时,子类中的其他元素必须位于基类的元素之后

你本质上有一个类似的架构(为清晰起见,删除了xml-y标签)

 base sequence prop1 prop3 derived1 extends base sequence  derived2 extends base sequence prop2 

没有办法在prop1和prop3之间粘贴占位符来指示派生xml的属性可以去哪里。

最后,您的数据格式与业务对象之间存在不匹配。 可能你最好的选择是定义一个对象来处理你的xml序列化。

例如

 [XmlRoot("Object") public class SerializableObjectForPersistance { [XmlElement(Order = 1)] public bool Property1 { get; set; } [XmlElement(Order = 2, IsNullable=true)] public bool Property2 { get; set; } [XmlElement(Order = 3)] public bool Property3 { get; set; } } 

这会将xml序列化代码与对象模型分开。 将所有值从SerializableObject1或SerializableObject2复制到SerializableObjectForPersistance,然后序列化它。

从本质上讲,如果您希望对序列化xml的格式进行这样的特定控制,而这种格式与期望的xml序列化框架并不相符,那么您需要解耦业务对象设计(在这种情况下为inheritance结构)以及序列化的责任。业务对象。

编辑: 这种方法不起作用 。 我已经离开了这个post,以便人们可以避免这种想法。

序列化器以递归方式执行。 这有好处; 在反序列化时,反序列化过程可以读取基类,然后读取派生类。 这意味着派生类上的属性未在基础上的属性之前设置,这可能导致问题。

如果它真的很重要(而且我不确定为什么按顺序排列这些很重要)那么你可以试试这个 –

1)使基类’Property1和Property3成为虚拟。 2)在派生类中使用普通属性覆盖它们。 例如

 public class SerializableBase { [XmlElement(Order = 1)] public virtual bool Property1 { get; set;} [XmlElement(Order = 3)] public virtual bool Property3 { get; set;} } [XmlRoot("Object")] public class SerializableObject1 : SerializableBase { } [XmlRoot("Object")] public class SerializableObject2 : SerializableBase { [XmlElement(Order = 1)] public override bool Property1 { get { return base.Property1; } set { base.Property1 = value; } } [XmlElement(Order = 2)] public bool Property2 { get; set;} [XmlElement(Order = 3)] public override bool Property3 { get { return base.Property3; } set { base.Property3 = value; } } } 

这将属性的具体实现放在最派生的类上,并且应该遵守该顺序。

看起来XmlSerializer类序列化基类型,然后按顺序序列化派生类型,并且仅单独尊重每个类中的Order属性。 尽管订单并不完全符合您的要求,但仍应正确反序列化。 如果你真的必须有这样的订单,你需要编写一个自定义的xml序列化程序。 我会提醒您,因为.NET XmlSerializer会为您做很多特殊处理。 你能按照你提到的顺序描述你为什么需要东西吗?

这篇文章现在已经很老了,但我最近在WCF中遇到了类似的问题,并找到了类似于Steve Cooper的解决方案,但是它确实有效,并且可能也适用于XML序列化。

如果从基类中删除XmlElement属性,并将具有不同名称的每个属性的副本添加到通过get / set访问基值的派生类,则可以使用XmlElementAttribute分配相应的名称来序列化副本。 ,然后希望以默认顺序序列化:

 public class SerializableBase { public bool Property1 { get; set;} public bool Property3 { get; set;} } [XmlRoot("Object")] public class SerializableObject : SerializableBase { [XmlElement("Property1")] public bool copyOfProperty1 { get { return base.Property1; } set { base.Property1 = value; } } [XmlElement] public bool Property2 { get; set;} [XmlElement("Property3")] public bool copyOfProperty3 { get { return base.Property3; } set { base.Property3 = value; } } } 

我还添加了一个接口来添加到派生类中,以便可以使副本成为必需的:

 interface ISerializableObjectEnsureProperties { bool copyOfProperty1 { get; set; } bool copyOfProperty2 { get; set; } } 

这不是必需的,但意味着我可以检查所有内容是否在编译时实现,而不是检查生成的XML。 我最初创建了SerializableBase的这些抽象属性,但这些属性首先序列化(使用基类),我现在意识到它是合乎逻辑的。

通过更改上面一行以通常的方式调用它:

 public class SerializableObject : SerializableBase, ISerializableObjectEnsureProperties 

我只是在WCF中对此进行了测试,并且已将该概念移植到XML Serialization而没有编译,所以如果这不起作用,请道歉,但我希望它的行为方式相同 – 我相信有人会让我知道如果不…

我知道这个问题已经过期了; 但是,这是一个解决此问题的方法:

方法的名称应始终以ShouldSerialize开头,然后以属性名称结束。 然后你只需要根据你想要的条件返回一个布尔值,关于是否序列化值。

 public class SerializableBase { public bool Property1 { get; set;} public bool Property2 { get; set;} public bool Property3 { get; set;} public virtual bool ShouldSerializeProperty2 { get { return false; } } } [XmlRoot("Object")] public class SerializableObject1 : SerializableBase { } [XmlRoot("Object")] public class SerializableObject2 : SerializableBase { public override bool ShouldSerializeProperty2 { get { return true; } } } 

使用SerializableObject2的结果:〜

      

使用SerializableObject1的结果:〜

     

希望这有助于其他许多人!

就像纳德所说的那样,也许可以考虑制作更松散耦合的设计。 但是,在我的情况下,松散耦合是不合适的。 这是我的类层次结构,以及我如何在不使用自定义序列化或DTO的情况下解决问题。

在我的项目中,我正在构建一大堆对象来表示将通过Web服务提交的XML文档片段。 有很多件。 并非所有请求都与每个请求一起发送(实际上,在此示例中,我正在建模响应,但概念是相同的)。 这些部分非常类似于构建块来组合请求(或者在这种情况下反汇编响应)。 所以这是一个使用聚合/封装来实现所需排序的例子,尽管有inheritance层次结构。

 [Serializable] public abstract class ElementBase { // This constructor sets up the default namespace for all of my objects. Every // Xml Element class will inherit from this class. internal ElementBase() { this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName(string.Empty, "urn:my-default-namespace:XSD:1") }); } [XmlNamespacesDeclaration] public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } } private XmlSerializationNamespaces _namespaces; } [Serializable] public abstract class ServiceBase : ElementBase { private ServiceBase() { } public ServiceBase(Guid requestId, Guid? asyncRequestId = null, Identifier name = null) { this._requestId = requestId; this._asyncRequestId = asyncRequestId; this._name = name; } public Guid RequestId { get { return this._requestId; } set { this._requestId = value; } } private Guid _requestId; public Guid? AsyncRequestId { get { return this._asyncRequestId; } set { this._asyncRequestId = value; } } private Guid? _asyncRequestId; public bool AsyncRequestIdSpecified { get { return this._asyncRequestId == null && this._asyncRequestId.HasValue; } set { /* XmlSerializer requires both a getter and a setter.*/ ; } } public Identifier Name { get { return this._name; } set { this._name; } } private Identifier _name; } [Serializable] public abstract class ServiceResponseBase : ServiceBase { private ServiceBase _serviceBase; private ServiceResponseBase() { } public ServiceResponseBase(Guid requestId, Guid? asyncRequestId = null, Identifier name = null, Status status = null) { this._serviceBase = new ServiceBase(requestId, asyncRequestId, name); this._status = status; } public Guid RequestId { get { return this._serviceBase.RequestId; } set { this._serviceBase.RequestId = value; } } public Guid? AsyncRequestId { get { return this._serviceBase.AsyncRequestId; } set { this._serviceBase.AsyncRequestId = value; } } public bool AsynceRequestIdSpecified { get { return this._serviceBase.AsyncRequestIdSpecified; } set { ; } } public Identifier Name { get { return this._serviceBase.Name; } set { this._serviceBase.Name = value; } } public Status Status { get { return this._status; } set { this._status = value; } } } [Serializable] [XmlRoot(Namespace = "urn:my-default-namespace:XSD:1")] public class BankServiceResponse : ServiceResponseBase { // Determines if the class is being deserialized. private bool _isDeserializing; private ServiceResponseBase _serviceResponseBase; // Constructor used by XmlSerializer. // This is special because I require a non-null List of items later on. private BankServiceResponse() { this._isDeserializing = true; this._serviceResponseBase = new ServiceResponseBase(); } // Constructor used for unit testing internal BankServiceResponse(bool isDeserializing = false) { this._isDeserializing = isDeserializing; this._serviceResponseBase = new ServiceResponseBase(); } public BankServiceResponse(Guid requestId, List responses, Guid? asyncRequestId = null, Identifier name = null, Status status = null) { if (responses == null || responses.Count == 0) throw new ArgumentNullException("The list cannot be null or empty", "responses"); this._serviceResponseBase = new ServiceResponseBase(requestId, asyncRequestId, name, status); this._responses = responses; } [XmlElement(Order = 1)] public Status Status { get { return this._serviceResponseBase.Status; } set { this._serviceResponseBase.Status = value; } } [XmlElement(Order = 2)] public Guid RequestId { get { return this._serviceResponseBase.RequestId; } set { this._serviceResponseBase.RequestId = value; } } [XmlElement(Order = 3)] public Guid? AsyncRequestId { get { return this._serviceResponseBase.AsyncRequestId; } set { this._serviceResponseBase.AsyncRequestId = value; } } [XmlIgnore] public bool AsyncRequestIdSpecified { get { return this._serviceResponseBase.AsyncRequestIdSpecified; } set { ; } // Must have this for XmlSerializer. } [XmlElement(Order = 4)] public Identifer Name { get { return this._serviceResponseBase.Name; } set { this._serviceResponseBase.Name; } } [XmlElement(Order = 5)] public List Responses { get { return this._responses; } set { if (this._isDeserializing && this._responses != null && this._responses.Count > 0) this._isDeserializing = false; if (!this._isDeserializing && (value == null || value.Count == 0)) throw new ArgumentNullException("List cannot be null or empty.", "value"); this._responses = value; } } private List _responses; } 

因此,虽然我必须为所有包含的类创建属性,但是当访问叶类的属性时,我可以通过简单地使用包含的类的属性来委托我在包含的类属性setter / getters中可能具有的任何自定义逻辑。 由于没有inheritance,我可以使用XmlElementAttribute属性修饰叶类的所有属性,并使用我认为合适的任何排序。


更新:

我回过头来重温这篇文章,因为我关于使用类inheritance的设计决定又回来了。 虽然我的上述解决方案确实有效,但我正在使用它,我认为Nader的解决方案是最好的,应该在我提出的解决方案之前考虑。 事实上,我今天正在为他做准备! 我真的很喜欢他的答案,如果我有机会重构我当前的项目,我肯定会将业务对象与对象的序列化逻辑分开,否则这些对象将从inheritance中获益,以简化代码并使其更容易供他人使用和理解。

感谢您发布您的回复Nader,因为我认为许多人会发现它非常有启发性和实用性。