为什么 WCF DataContract 不按字母顺序序列化成员?

3

我有几个类似于以下代码片段的 DataContract

[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
    [DataMember(IsRequired = true)]
    public string Name { get; set; }

    [DataMember]
    public string Value { get; set; }

    [DataMember(IsRequired = true)]
    public int Id { get; set; }

    public ExtensionDataObject ExtensionData { get; set; }
}

之前我没有注意到序列化消息,但最近进行了两个更改:我添加了一个名为ReturnCode的新属性,并运行CodeMaid的“重新组织”功能,使属性按字母顺序排列。

现在它看起来像这样:

[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
    public ExtensionDataObject ExtensionData { get; set; }

    [DataMember(IsRequired = true)]
    public int Id { get; set; }

    [DataMember(IsRequired = true)]
    public string Name { get; set; }

    [DataMember]
    public int ReturnCode { get; set; }

    [DataMember]
    public string Value { get; set; }
}

根据微软有关“数据契约成员顺序”页面(http://msdn.microsoft.com/en-us/library/ms729813(v=vs.110).aspx)的说明,我意识到“ReturnCode”会破坏契约,因为序列化程序会在“Value”之前插入它。因此,我添加了一个“Order”属性值,假定原始顺序是字母顺序,最终代码如下:
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
    public ExtensionDataObject ExtensionData { get; set; }

    [DataMember(IsRequired = true, Order = 0)]
    public int Id { get; set; }

    [DataMember(IsRequired = true, Order = 1)]
    public string Name { get; set; }

    [DataMember(Order = 3)]
    public int ReturnCode { get; set; }

    [DataMember(Order = 2)]
    public string Value { get; set; }
}

然而,这引发了一个异常,即反序列化的成员顺序不正确。我回滚到以前的变更集,即所有变更之前,并且确实在 SOAP 请求中(通过Fiddler查看)成员的原始顺序不是按字母顺序排序的,而是按代码中表示的原始顺序排序,即:NameValueId

我目前正在添加Order值到所有旧的DTO类型中,以根据它们以前的、未按字母排序的属性排列功能对它们进行排序。我想知道为什么序列化器使用编码顺序而不是按字母顺序排序?微软的规则说:

接下来是当前类型的数据成员,没有设置DataMemberAttribute属性的Order属性,按字母顺序排列

更新:

在我添加了Order值以根据它们的原始顺序对属性进行排序之后,我再次运行Fiddler,它仍然使用项目按字面编码的顺序。换句话说,由于某种原因,我的 WCF 服务完全忽略了任何序列化排序逻辑,并且只是按它们出现在 .cs 文件中的顺序对属性进行排序。实际上,我能够使其正确序列化的唯一方法是将每个类型中的属性物理重新排列到它们的原始顺序。虽然这样可以工作,但这不是首选方式。

更新2-解决方案:

根据Dracor的建议,我向我的DTO添加了[XmlElement(Order = 1)]属性和一个XmlRootAttribute。SOAP序列化确实遵循了这些属性分配的顺序。我没有考虑过,但我的服务确实使用Castle DynamicProxy,所以我猜它正在将序列化程序从DataContractSerializer更改为XmlSerializer


你使用的序列化程序是什么?请参考此问题,了解序列化程序的影响。http://stackoverflow.com/questions/20004946/c-sharp-datamember-serializer-ordering-opposite-of-expected - jordanhill123
这是由WCF服务生成的SOAP消息,因此应该使用XML DataContractSerializer - Nick Gotch
1个回答

2
为什么不直接使用XmlSerializer来序列化/反序列化您的XML呢?它比DataContractSerializer更容易使用,并且在大多数情况下都可以正常工作。

问题在于我只控制序列化方面;客户已经有了自己的SOAP客户端和反序列化器。我可以改变我的客户端,但这并不能解决问题。 - Nick Gotch
1
但是,为什么会成为问题呢?如果您使用它来序列化数据,您可以分配命名空间(通过构造函数的第二个参数)以及序列化的顺序(即[XmlElementAttribute(Order = 1)] - 注意:您也可以尝试在当前类和序列化器中使用此属性。我不会感到惊讶如果它能够正常工作)。 - Kanadaj

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