.NET Serialization Ordering

25,763

Solution 1

Technically, from a pure xml perspective, I would say that this is probably a bad thing to want to do.

.NET hides much of the complexity of things like XmlSerialization - in this case, it hides the schema to which your serialized xml should conform.

The inferred schema will use sequence elements to describe the base type, and the extension types. This requires strict ordering -- even if the Deserializer is less strict and accepts out of order elements.

In xml schemas, when defining extension types, the additional elements from the child class must come after the elements from the base class.

you would essentially have a schema that looks something like (xml-y tags removed for clarity)

base
  sequence
    prop1
    prop3

derived1 extends base
  sequence
    <empty>

derived2 extends base
  sequence
    prop2

There's no way to stick a placeholder in between prop1 and prop3 to indicate where the properties from the derived xml can go.

In the end, you have a mismatch between your data format and your business object. Probably your best alternative is to define an object to deal with your xml serialization.

For example

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

This separates your xml serialization code from your object model. Copy all the values from SerializableObject1 or SerializableObject2 to SerializableObjectForPersistance, and then serialize it.

Essentially, if you want such specific control over the format of your serialized xml that doesn't quite jive with the expectations xml serialization framework, you need to decouple your business object design (inheritance structure in this case) and the responsibility for serialization of that business object.

Solution 2

EDIT: This approach doesn't work. I've left the post in so that people can avoid this line of thinking.

The serializer acts recursively. There's a benefit to this; on deserialization, the deserialization process can read the base class, then the derived class. This means that a property on the derived class isn't set before the properties on the base, which could lead to problems.

If it really matters (and I'm not sure why it's important to get these in order) then you can try this --

1) make the base class' Property1 and Property3 virtual. 2) override them with trivial properties in your derived class. Eg

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

}

This puts a concrete implementtion of the property on the most derived class, and the order should be respected.

Solution 3

This post is quite old now, but I had a similar problem in WCF recently, and found a solution similar to Steve Cooper's above, but one that does work, and presumably will work for XML Serialization too.

If you remove the XmlElement attributes from the base class, and add a copy of each property with a different name to the derived classes that access the base value via the get/set, the copies can be serialized with the appropriate name assigned using an XmlElementAttribute, and will hopefully then serialize in the default order:

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

I also added an Interface to add to the derived classes, so that the copies could be made mandatory:

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

This is not essential but means that I can check everything is implemented at compile time, rather than checking the resultant XML. I had originally made these abstract properties of SerializableBase, but these then serialize first (with the base class), which I now realise is logical.

This is called in the usual way by changing one line above:

public class SerializableObject : SerializableBase, ISerializableObjectEnsureProperties

I've only tested this in WCF, and have ported the concept to XML Serialization without compiling, so if this doesn't work, apologies, but I would expect it to behave in the same way - I'm sure someone will let me know if not...

Solution 4

It looks like the XmlSerializer class serializes the base type and then derived types in that order and is only respecting the Order property within each class individually. Even though the order is not totally what you want, it should still Deserialize properly. If you really must have the order just like that you will need to write a custom xml serializer. I would caution against that beacuse the .NET XmlSerializer does a lot of special handling for you. Can you describe why you need things in the order you mention?

Solution 5

I know this question has expired; however, here is a solution for this problem:

The name of the method should always begin with ShouldSerialize and then end with the property name. Then you simply need to return a boolean based on whatever conditional you want, as to whether to serialize the value or not.

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

The outcome when using SerializableObject2: ~

<Object>
    <Property1></Property1>
    <Property2></Property2>
    <Property3></Property3>
</Object>

The outcome when using SerializableObject1: ~

<Object>
    <Property1></Property1>
    <Property3></Property3>
</Object>

Hope this helps many others!

Share:
25,763
Chris Knight
Author by

Chris Knight

Keen Agilist specialising in C# and MVC

Updated on July 14, 2022

Comments

  • Chris Knight
    Chris Knight almost 2 years

    I am trying to serialize some objects using XmlSerializer and inheritance but I am having some problems with ordering the outcome.

    Below is an example similar to what I have setup: ~

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

    The outcome I want is as follows: ~

    <Object>
        <Property1></Property1>
        <Property2></Property2>
        <Property3></Property3>
    </Object>
    

    However I am getting an outcome of: ~

    <Object>
        <Property1></Property1>
        <Property3></Property3>
        <Property2></Property2>
    </Object>
    

    Does anyone know if it is possible or of any alternative?

    Thanks

  • Steve Cooper
    Steve Cooper almost 15 years
    Ah, sorry -- I would have thought it had worked by looking at the class that contained a concrete implementation, but clearly not. I'll update the post to indicate that this approach doesn't work.
  • fourpastmidnight
    fourpastmidnight over 11 years
    I'm having the exact same problem. Actually, inheritance with [XmlElement(Order = n)] does work, but only for a parent->child relationship, and nothing greater, i.e. it doesn't work for grandparent->parent->child. I'm going to try a modified version of this solution to see if it works (because I think it will work) and I'll report back.
  • fourpastmidnight
    fourpastmidnight over 11 years
    Well, my idea(s) didn't work :). But I have a partial solution. I have about 10 objects, most of which have the same properties (some are missing one or two here and there), so I have a class hierarchy to keep it DRY. I still want to keep it DRY, but, as we know, Xml Serialization doesn't order things "correctly". So, instead of using inheritance, use aggregation. In other words, derived objects should "have a" base object as a class member. This way, you can encapsulate all the functionality in the "base class" and merely expose it from your "derived classes". Not elegant, but it will work.
  • fourpastmidnight
    fourpastmidnight over 11 years
    You make a very good point as to why ordering doesn't behave "as expected", because we're thinking in terms of OOP and not XML Schemas. In my paricular case, loose-coupling is not an appropriate design (again, this is niche--always strive for loose-coupling!), and if that's your situation, you could always try aggregation, where the "child" object contains the "parent" object. You still achieve encapsulation and reusability, but you can also specify, for the child, the exact ordering of elements.
  • fourpastmidnight
    fourpastmidnight over 11 years
    @Nader I referenced your answer in my answer to this question. While my solution works, yours is clearly superior. I was incorrect when saying loose-coupling was not appropriate in my design. If I ever have the chance to refactor, I'm using your recommendations!
  • Ian1971
    Ian1971 over 9 years
    This does actually work. However, you've essentially added Property2 into classes it doesn't belong in (even though it might not appear in the xml).
  • The Dag
    The Dag over 8 years
    I beg to differ. Yes, the schema is important. But the object is not the schema. It may be a bit unusual, but there's nothing inherently wrong in defining your objects in some other way than a one-to-one map to your messages. The whole point with XmlElementAttribute.Order is to be able to define the order of elements in a sequence. It does not behave as specified - which should indeed have resulted in the output the OP expected. Plainly, it's a bug!
  • The Dag
    The Dag over 8 years
    "a property on the derived class isn't set before the properties on the base, which could lead to problems". Not if the class is properly designed. Microsoft's own guidelines state "Note that a type's properties should be able to be set and retrieved in any order."