开发者

DataContractSerializer and its issues - search for better serializer

开发者 https://www.devze.com 2023-01-10 03:13 出处:网络
We\'ve already established previously that DCS serializes/deserializes objects alphabetically. However, after further investigation I\'ve discoverred this is not entirely true.

We've already established previously that DCS serializes/deserializes objects alphabetically. However, after further investigation I've discoverred this is not entirely true.

If we have a structure like this:

[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; 
}

and have an object of type TextContent to serialize, DCS will do this:

<Content i:type="TextContent" ...>
<Description>de开发者_如何学运维sc</Description>
<Slug>some-slug</Slug>
<Title>content title</Title>
<Text>some content</Text>
</Content>

So as you can see, the property from the inheriting class is attached to the end of the serialized XML fragment even though it should be before Title. DCS doesn't go over the combined properties and reorder them. I've noticed this when I was manually adding Text element in front of Title element and deserialization just didn't want to work. That's why I performed a serialization of a new object and figured this out.

My questions are:

  1. This can't be common knowledge?? Anyone else noticed this?
  2. Anyone knows of a better serializer (all I ever find if I search for it is the old XmlSerializer and DCS) because this issue with DCS's ordering is extremely annoying? I know we can use the Order attribute but that only enables us to align with one external XML source. What if we have three, four or more third party XML providers which all generate perfectly valid XML but our app is nitpicking about elements order (because of DCS)?


The base types are always first in the order. You can define the order of the serialized properties of a object with respect of the Order property of the DataMember attribute (see http://msdn.microsoft.com/en-us/library/ms729813.aspx)


There's the NetDataContractSerializer but the only difference between it and the DCS is that it enables type sharing between the client and the server but you lose the forward compatibility because both sides have to serialize/deserialize into the same type..

There's also the C# wrapper for protocol buffer on codeplex: http://code.google.com/p/protobuf-net/

I haven't tried it out myself, but it's supposed to be much faster and lightweight. As to your actual questions:

  1. doubt it, I certainly never noticed this :-P
  2. can you give an example where the ordering of elements actually mattered? I haven't come across this myself (I guess that's why most of us haven't noticed this behavior..), but with the proto-buf serializer this will undoubtly be a problem..


If you need to be able to serialize to match an external schema, then you obviously shouldn't be using the DataContractSerializer. That's not what it's for.

You can either use the XmlSerializer, which is intended to give you more control over the serialized XML, or implement IXmlSerializable, and gain complete control over the XML, or you can write your own custom serialization using LINQ to XML. This will let you do exactly what you mention - serialize the same data in different ways. Example:

Data

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; }
}

Test Program

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");
}

Serialization as Elements:

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)
        );
}

Serialization as Attributes:

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>
0

精彩评论

暂无评论...
验证码 换一张
取 消