【问题标题】:DataContractSerializer and its issues - search for better serializerDataContractSerializer 及其问题 - 寻找更好的序列化器
【发布时间】:2010-08-02 21:59:59
【问题描述】:

我们已经建立了previously,DCS 按字母顺序序列化/反序列化对象。但是,经过进一步调查,我发现这并非完全正确。

如果我们有这样的结构:

[DataContract]
public class Content
{
    [DataMember]
    public string Title;
    [DataMember]
    public string Slug;
    [DataMember]
    public string Description;
}

[DataContract]
public class TextContent : Content
{
    [DataMember]
    public string Text; 
}

并且有一个 TextContent 类型的对象要序列化,DCS 会这样做:

<Content i:type="TextContent" ...>
<Description>desc</Description>
<Slug>some-slug</Slug>
<Title>content title</Title>
<Text>some content</Text>
</Content>

如您所见,继承类的属性附加到序列化 XML 片段的末尾,即使它应该在 Title 之前。 DCS 不会检查组合属性并重新排序它们。 当我在 Title 元素前面手动添加 Text 元素并且反序列化不想工作时,我注意到了这一点。这就是为什么我执行了一个新对象的序列化并解决了这个问题。

我的问题是:

  1. 这不可能是常识??其他人注意到这一点了吗?
  2. 任何人都知道更好的序列化程序(如果我搜索它,我只能找到旧的 XmlSerializer 和 DCS),因为 DCS 的排序问题非常烦人?我知道我们可以使用 Order 属性,但这只能使我们与一个外部 XML 源保持一致。如果我们有三个、四个或更多第三方 XML 提供程序,它们都生成完全有效的 XML,但我们的应用程序在元素顺序上挑剔(因为 DCS)?

【问题讨论】:

    标签: c# xml-serialization .net-4.0


    【解决方案1】:

    基本类型始终排在第一位。您可以根据DataMember 属性的Order 属性定义对象的序列化属性的顺序(请参阅http://msdn.microsoft.com/en-us/library/ms729813.aspx

    【讨论】:

    • 我想这是正确的答案,但没有解释为什么如果继承类型具有按字母顺序排列在某些基类型属性前面的属性,为什么基类型在顺序中排在首位。我想这就是 DCS 的方式......
    • msdn.microsoft.com/en-us/library/ms729813.aspx 中,您可以在“基本规则”列表中看到:“如果数据协定类型是继承层次结构的一部分,则其基本类型的数据成员始终排在第一位."。所以它只是数据合约实现的一部分。所以没有解释,只是一个事实。
    【解决方案2】:

    NetDataContractSerializer,但它与 DCS 的唯一区别在于它启用了客户端和服务器之间的类型共享,但您失去了前向兼容性,因为双方都必须序列化/反序列化为相同的类型..

    codeplex 上还有用于协议缓冲区的 C# 包装器: http://code.google.com/p/protobuf-net/

    我自己没有尝试过,但它应该更快更轻量级。至于您的实际问题:

    1. 怀疑,我当然从来没有 注意到了这一点:-P
    2. 你能举一个元素的顺序实际上很重要的例子吗?我自己还没有遇到过这种情况(我想这就是我们大多数人没有注意到这种行为的原因..),但是对于 proto-buf 序列化程序,这无疑是一个问题..

    【讨论】:

    • 如果您查看我链接到的问题(之前的链接),您将能够继续使用示例甚至博客文章,其中作者详细解释了这个问题并且很容易重现- 只需重新排序序列化 XML 中的一些元素并尝试反序列化,您将得到不一致的行为(对象属性可能为 null 或具有奇怪的值,即使在您的 XML 中一切都很好)。我知道这一点,但现在我注意到了更多问题,如上面关于基类和子类的示例所述。
    • protobuf 看起来很有趣,从来不知道有这样的东西存在,但它是二进制的,所以我现在不能使用它。
    【解决方案3】:

    如果您需要能够序列化以匹配外部架构,那么您显然不应该使用DataContractSerializer。这不是它的用途。

    您可以使用XmlSerializer,它旨在让您更好地控制序列化的 XML,或者实现IXmlSerializable,并获得对 XML 的完全控制,或者您可以使用 LINQ 编写自己的自定义序列化来XML。这将使您完全按照您提到的方式进行操作 - 以不同的方式序列化相同的数据。示例:

    数据

    internal class Person
    {
        internal string Name { get; set; }
        internal string Telephone { get; set; }
        internal Address HomeAddress { get; set; }
        internal Address WorkAddress { get; set; }
    }
    
    internal class Address
    {
        internal string Line1 { get; set; }
        internal string Line2 { get; set; }
        internal string City { get; set; }
        internal string State { get; set; }
        internal string PostalCode { get; set; }
    }
    

    测试程序

    private static void Main()
    {
        var person = new Person
                         {
                             Name = "John Saunders",
                             Telephone = "something",
                             HomeAddress = new Address
                                               {
                                                   Line1 = "Line 1",
                                                   Line2 = "Line 2",
                                                   City = "SomeCity",
                                                   State = "SS",
                                                   PostalCode = "99999-9999",
                                               },
                             WorkAddress = new Address
                                               {
                                                   Line1 = "Line 1a",
                                                   Line2 = "Line 2a",
                                                   City = "SomeCitay",
                                                   State = "Sa",
                                                   PostalCode = "99999-999a",
                                               },
                         };
        XDocument personWithElements = SerializeAsElements(person);
        personWithElements.Save("PersonWithElements.xml");
    
        XDocument personWithAttributes = SerializeAsAttributes(person);
        personWithAttributes.Save("PersonWithAttributes.xml");
    }
    

    序列化为元素:

    private static XDocument SerializeAsElements(Person person)
    {
        return new XDocument(
            new XElement("Person",
                         new XElement("Name", person.Name),
                         new XElement("Telephone", person.Telephone),
                         SerializeAddressAsElements(person.HomeAddress, "HomeAddress"),
                         SerializeAddressAsElements(person.WorkAddress, "WorkAddress"))
            );
    }
    
    private static XElement SerializeAddressAsElements(Address address, string elementName)
    {
        return new XElement(elementName,
                            new XElement("Line1", address.Line1),
                            new XElement("Line2", address.Line2),
                            new XElement("City", address.City),
                            new XElement("State", address.State),
                            new XElement("PostalCode", address.PostalCode)
            );
    }
    

    序列化为属性:

    private static XDocument SerializeAsAttributes(Person person)
    {
        return new XDocument(
            new XElement("Person",
                         new XAttribute("Name", person.Name),
                         new XAttribute("Telephone", person.Telephone),
                         SerializeAddressAsAttributes(person.HomeAddress, "HomeAddress"),
                         SerializeAddressAsAttributes(person.WorkAddress, "WorkAddress"))
            );
    }
    
    private static XElement SerializeAddressAsAttributes(Address address, string elementName)
    {
        return new XElement(elementName,
                            new XAttribute("Line1", address.Line1),
                            new XAttribute("Line2", address.Line2),
                            new XAttribute("City", address.City),
                            new XAttribute("State", address.State),
                            new XAttribute("PostalCode", address.PostalCode)
            );
    }
    

    PersonWithElements:

    <?xml version="1.0" encoding="utf-8"?>
    <Person>
      <Name>John Saunders</Name>
      <Telephone>somethine</Telephone>
      <HomeAddress>
        <Line1>Line 1</Line1>
        <Line2>Line 2</Line2>
        <City>SomeCity</City>
        <State>SS</State>
        <PostalCode>99999-9999</PostalCode>
      </HomeAddress>
      <WorkAddress>
        <Line1>Line 1a</Line1>
        <Line2>Line 2a</Line2>
        <City>SomeCitay</City>
        <State>Sa</State>
        <PostalCode>99999-999a</PostalCode>
      </WorkAddress>
    </Person>
    

    PersonWithAttributes:

    <?xml version="1.0" encoding="utf-8"?>
    <Person Name="John Saunders" Telephone="somethine">
      <HomeAddress Line1="Line 1" Line2="Line 2" City="SomeCity" State="SS" PostalCode="99999-9999" />
      <WorkAddress Line1="Line 1a" Line2="Line 2a" City="SomeCitay" State="Sa" PostalCode="99999-999a" />
    </Person>
    

    【讨论】:

    • 到目前为止,我在一个项目中使用了 LINQ to XML,效果很好。
    猜你喜欢
    • 2019-02-14
    • 2015-11-24
    • 2019-03-08
    • 1970-01-01
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多