1

I'm using XmlSerializer. A big class instance is being serialized, and that big one aggregates (I'd say pretty much owns) smaller classes with some inheritance involved. It appears that in base classes, some properties are crucial and are used to mirror their state when being serialized. But for some of their subclasses those properties should be suppressed, because other, more readable ones, were added.

Here's an example (not real code sample):

class Base
{
    public int x { get; set; }
    // ...other stuff...
}

class Derived1 : Base
{
    // just some added functions
    // it's perfectly fine serializing base.x
    // ...other stuff...
}

class Derived2 : Base
{
    public int y { get; set; } // will in the end be populating this.y 
    // and base.x, but is more readable and has more sense in XML
    // ...other stuff...
}

class BigClass
{
    public List<Derived1> List1 { get { return m_List1; } }
    private List<Derived1> m_List1 = new List<Derived1>();

    public List<Derived2> List2 { get { return m_List2; } }
    private List<Derived2> m_List2 = new List<Derived2>();

    // ...other stuff...
}

I've read this for the topic of using XmlAttributeOverrides. Apparently, it works for situations like XmlSerializer s = new XmlSerializer(typeof(Derived2), overrides). However, when I try to use it with BigClass:

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes ignore = new XmlAttributes { XmlIgnore = true };
overrides.Add(typeof(Derived2), "x", ignore);
XmlSerializer serializer = new XmlSerializer(typeof(BigClass), overrides);

// and, finally, save it
using (TextWriter tw = new StreamWriter("output.xml"))
{
    serializer.Serialize(tw, SomeBigClassInstance);
}

the output still contains the x property for both types. The Lists contain them by reference to the appropriate subclass, so it's not like XmlSerializer cannot see their actual class. Despite the overrides, the sample xml output would look like

<?xml version="1.0" encoding="utf-8"?>
<BigClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <List1>
    <Derived1>
      <x>1</x>
      <!-- other stuff -->
    </Derived1>
  </List1>
  <List2>
    <Derived2>
      <x>2</x>  <!-- unwanted guy -->
      <y>20</y>
      <!-- other stuff -->
    </Derived2>
  </List2>
  <!-- other stuff -->
</BigClass>

So the question is: what am I doing wrong here? Does this method of suppressing unwanted properties only work when serializer is deliberately applied to the node of this class only?

Note. The savings in my real case are bigger than just one line. There are more properties to be hidden like this, and lists can contain decently large amount of items. The concern is readability and to some extent, manual modifiability of the resulting xml-files. Apparently, recalculating x properties or deleting them by hand is tiresome enough to attempt such endeavor.

Update 1. Trying to add code like

overrides.Add(typeof(List<Derived2>), "x", ignore);

doesn't seem to fix it. There are actually more nodes from BigClass down to Derived1/2 in the aggregation hierarchy. And adding them all from Derived2 up to the root didn't help. So I'm assuming this proposal won't fix it even in this simplified case. (Update 2. It won't).

Community
  • 1
  • 1
  • i am not completely sure but I think this is the correction: `overrides.Add(typeof(List), "x", ignore);` but again, I am not sure if this will work since the typeof() will return the generic collection... – MaxOvrdrv Dec 24 '15 at 13:12
  • @MaxOvrdrv The real case actually has more "depth". I've added code like `overrides.Add(typeof(List), "x", ignore);` for every node that I was going "up" the aggregation hierarchy, but it doesn't help. I'll update the question description. – S. Kalabukha Dec 24 '15 at 13:16
  • The [answer accepted](http://stackoverflow.com/q/2247086/395718) for a different question may help. It mentions additional attributes `XmlArrayAttribute` and `XmlArrayItemAttribute`. – Dialecticus Dec 24 '15 at 13:19
  • @Dialecticus The `XmlArrayItemAttribute` has eight properties, but I don't understand how changing the node name, namespace, type, typeId etc. can help here... Would you be so kind to elaborate? – S. Kalabukha Dec 24 '15 at 13:38
  • I don't know either. It just "smells" to me like a path to a solution. That's why I wrote a comment instead of an answer. – Dialecticus Dec 24 '15 at 13:42
  • @Dialecticus Ah, ok, I'll look into this more later. So far I haven't found a way to add this "ignore" attribute into `XmlArrayItemAttribute`. – S. Kalabukha Dec 24 '15 at 13:52
  • I have figured out a way to achieve what I want, but it might not be applicable for everyone's use case (and it is a far stretch for me too). I'll post it as an answer later when I get home. I will change its accepted status if someone comes up with a better idea later on. – S. Kalabukha Dec 25 '15 at 13:59

1 Answers1

1

(Disclaimer. Some people might say that serialized fields are like public interface of the class in question, so hiding them in derived classes is kind of a code smell. But I'm not going to address interface implications here, only the way the said goal can achieved.)

There are two features of XmlSerializer that allow to exclude some properties from the output file:

  1. <field>Specified pattern (AKA the old way), e.g. like in here: How to make a value type nullable with .NET XmlSerializer?
  2. ShouldSerialize<field>() pattern, e.g. like here: Xml serialization - Hide null values

Both have some pros and cons in the removing redundant fields down the inheritance tree. Let's see:

<field>Specified pattern. Usage:

public class Example
{
    public int x { get; set; }

    [XmlIgnore]
    public bool xSpecified;

    public Example()
    {
        xSpecified = <set your value for this class>;
    }
}

Pros:

  • Can be set in other classes in case if the logic demands it

Cons:

  • Breaks encapsulation of the class in question
  • Contributes to object's size in memory, one boolean per object per property
  • You have to remember to set it to appropiate value, e.g. in constructor

ShouldSerialize<field>() pattern. Usage:

public class Example
{
    public int x { get; set; }

    public virtual bool ShouldSerializex()
    {
        return <your logic expression here>;
    }
}

Pros:

  • Keeps the encapsulation of the class, allows the logic to be contained in it
  • No additional memory per property
  • More flexible overall

Cons:

  • In this case, having to make it virtual so it would work with derived classes as well, so possibly additional memory for vtable pointer in each object

For the particular issue of mine I utilized both approaches in different places. Self-sufficient classes with several properties needing to be hidden get more advantage out of #2, especially if the project has lists upon lists of lots of their instances. Small classes used to be "brick stones" for composing larger ones might be "dumb" enough to be ignorant whether to serialize some stuff or not, which makes #1 a possible option.

Community
  • 1
  • 1