In my project I had been using a set of arrays to hold some data, realized it was a bad design, so then I decided to create an object that holds all the relevant data, and add them to an arraylist, this work great except all the data was information that extended a complicated dynamic button. So then, since the button was a custom user control anyway. I just added the fields i needed to store to the button class its self and everything was simple and right with the world.

then I come to the problem of saving the date, XML serialization is the method of choice, but I have a problem, This would want to save ALL the public properties of the button, not just the custom ones, so I ask.

Is there a way to denote that just these X amount of properties be serialized without going through and overriding all the properties in the base class and and adding a bracket that asks for it to be omitted?

alternatively, say I created a class that just holds my data properties and had my button class additionally inherit from that. giving it those fields, would there be a way to just serialize the members gained from that 2nd inheritance? (since # only supports 1 baseclass I refer of course to a linear inheritance) even still I just don't think this is the solution, I just don't know how to go about this, Other than go back to a separate data object.

Recommended Answers

All 8 Replies

No -- you either need to handle the reflection yourself, serialize all the data, or mark the members you want serialized. Here is how you can serialize the data yourself. This is on a form with two buttons:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace daniweb
{
  public partial class frmSerial2 : Form
  {
    private byte[] serializedData;

    public frmSerial2()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      ConcreteClass cc = new ConcreteClass();
      cc.BaseProperty1 = "abc";
      cc.BaseProperty2 = 5;
      cc.BaseProperty3 = DateTime.Now;

      cc.Property1 = "ssss";
      cc.Property2 = 15;
      cc.Property3 = DateTime.Now.AddDays(5);
      cc.Property4 = 1324;

      using (MemoryStream ms = new MemoryStream())
      {
        CustomSerializer.SerializeObject(ms, cc);
        serializedData = ms.ToArray();
      }

      #region lets inspect our work
      {
        string xmlData = System.Text.ASCIIEncoding.UTF8.GetString(serializedData);
        Console.WriteLine(xmlData);
        System.Diagnostics.Debugger.Break();
      }
      #endregion
    }

    private void button2_Click(object sender, EventArgs e)
    {
      ConcreteClass cc = new ConcreteClass();
      using (MemoryStream ms = new MemoryStream(serializedData))
      {
        ms.Position = 0;
        ms.Seek(0, SeekOrigin.Begin);
        CustomSerializer.DeserializeObject(ms, cc);
      }
      System.Diagnostics.Debugger.Break();
    }
  }

  public class BaseClass
  {
    public string BaseProperty1 { get; set; }
    public int BaseProperty2 { get; set; }
    public DateTime BaseProperty3 { get; set; }

    public BaseClass()
    {
    }
  }

  public class ConcreteClass : BaseClass
  {
    [CustomSerialized]
    public string Property1 { get; set; }
    [CustomSerialized]
    public int Property2 { get; set; }
    [CustomSerialized]
    public DateTime Property3 { get; set; }
    public long Property4 { get; set; }

    public ConcreteClass()
      : base()
    {
    }
  }

  [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
  internal sealed class CustomSerialized : Attribute
  {
    public CustomSerialized()
    {
      
    }
  }

  public class NameValuePair
  {
    public string PropertyName { get; set; }
    public object PropertyValue { get; set; }
    public NameValuePair() { }
    public NameValuePair(string PropertyName, object PropertyValue)
      : this()
    {
      this.PropertyName = PropertyName;
      this.PropertyValue = PropertyValue;
    }
  }

  public static class CustomSerializer
  {

    //.DeclaredOnly - ignored inherited members
    private const BindingFlags SqlObjectBindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

    private static bool IsMarkedCustomSerialzed(PropertyInfo Prop)
    {
      Attribute[] attrs = System.Attribute.GetCustomAttributes(Prop);
      foreach (Attribute at in attrs)
      {
        if ((at as CustomSerialized) != null)
          return true;
      }
      return false;
    }

    private static PropertyInfo[] GetProperties(object o)
    {
      List<PropertyInfo> result = new List<PropertyInfo>();
      PropertyInfo[] properties = o.GetType().GetProperties(SqlObjectBindingFlags);
      foreach (PropertyInfo pi in properties)
      {
        if (IsMarkedCustomSerialzed(pi) && pi.CanRead && pi.CanWrite)
          result.Add(pi);
      }
      return result.ToArray();
    }

    public static void SerializeObject(Stream stream, object o)
    {
      PropertyInfo[] properties = GetProperties(o);
      List<NameValuePair> lst = new List<NameValuePair>();
      foreach (PropertyInfo pi in properties)
      {
        lst.Add(new NameValuePair(pi.Name, pi.GetValue(o, null)));
      }
      XmlSerializer ser = new XmlSerializer(typeof(List<NameValuePair>));
      ser.Serialize(stream, lst);
    }

    //This doesn't create the object for you since we're not serializing all properties.
    //It basically does "apply property changes"
    public static void DeserializeObject(Stream s, object o)
    {
      XmlSerializer ser = new XmlSerializer(typeof(List<NameValuePair>));
      List<NameValuePair> lst = (List<NameValuePair>)ser.Deserialize(s);
      PropertyInfo[] properties = GetProperties(o);
      foreach (PropertyInfo pi in properties)
      {
        NameValuePair nvp = FindInList(lst, pi.Name);
        if (nvp != null)
        {
          pi.SetValue(o, nvp.PropertyValue, null);
        }
      }
    }
    private static NameValuePair FindInList(List<NameValuePair> searchList, string PropertyName)
    {
      foreach (NameValuePair nvp in searchList)
      {
        if (string.Compare(nvp.PropertyName, PropertyName, true) == 0)
          return nvp;
      }
      return null;
    }
  }
}
commented: amazing code! +3

Scott you never cease to amaze me! This is some great code, let me ask so questions to make sure I got it.

these Bindingflags allow you to tell the serializer to only serialize the public members declared in that object, not the inherited members?
--if so this is awesome.

and could this be modified to serialize a list of classes?

from what I can follow the data is saved just as pairs of property names and the values of each, so multiple objects that have the same property names... just not sure how I could handle that.

Thanks!

>> these Bindingflags allow you to tell the serializer to only serialize the public members declared in that object, not the inherited members? --if so this is awesome.

Yes. This only does properties (have get/set, not fields).

>>and could this be modified to serialize a list of classes?

Yes. I almost always serialize List<class>

from what I can follow the data is saved just as pairs of property names and the values of each, so multiple objects that have the same property names... just not sure how I could handle that.

You can't have multiple property names the same. IE a button doesn't have two properties called "Text", it can only have a single property. The only exception to this is when you inherit a class you can override the member -- but regardless it is still a single property name/value pair. You can also hide inherited members with the new keyword, but again this doesn't apply.

The reason this doesn't apply is because you said you wanted to serialize X properties in a class and ignore inheritence so I cannot think of a single way where this would be a problem.

Thanks for the reply!

what i meant was, your serializer/dematerializer methods accept an object. but that object is expected to be a class with members, I need those functions to accept a list of objects. not just an object.

I can't seem to figure out how to get it to work right. I got a result, but the XML file is then fairly confusing and for some reason the resulting XML file's objects are always backwards, as in the last object in the list is first in the xml file.

public static void SerializeObject(Stream stream, List<object> objlist)
        {

            List<List<NameValuePair>> newObjList = new List<List<NameValuePair>>();
            
            foreach (object o in objlist)
            {
                PropertyInfo[] properties = GetProperties(o);
                List<NameValuePair> lst = new List<NameValuePair>();
                foreach (PropertyInfo pi in properties)
                {
                    lst.Add(new NameValuePair(pi.Name, pi.GetValue(o, null)));
                }
                newObjList.Add(lst);
            }
            //now I have a lists of lists right? now what do I do do I serialize each one separately then add the resulting stream to another serializer? I'm kind of at a loss. this serialization thing is new to me.

            //or would I just serialize the list of lists and this actually work? like?

            XmlSerializer ser = new XmlSerializer(typeof(List<List<NameValuePair>>));
            ser.Serialize(stream, newObjList);
        }

note, code is modified from the code you posted previously.

and as far as the deserialzing such a thing. I can't even get started.

-->a briefing of my final goal is to have a bar of dynamically created buttons with special values that need to be saved. then when the app opens back up i need it to parse the xml to return a list of objects that I can loop through and create a new special button for each one, assigning the special values to each one as it goes.

-->stuck in the mud at the first corner I'm afraid, but you did an amazing job getting me to the starting line.

So mark the special's button class with the attribute I gave you and serialize the button. I don't understand the need for a double list.

because the buttons are not predefined. they are created dynamically by a user. I can't hardcode them. they will have to be added to a list upon creation. then multiple instances of this class will need to be serialized.

I can't seen any other way to do it besides serializing a list of lists!

am I wrong?

Alright! I figured it out. I modified your deserialize method to return a list of custom objects contains just the extra data members that will be marked in the extendedbutton control class that way I can loop through them and create my buttons form them. I used all concept code because I don't like practicing in my real projects but I decided I would post the result for learning purposes for future visitors to the site.

Thanks for everything sknake! without your help I'm not sure I could have done it.

modified deserialize method.

public static List<extrabutondata> DeserializeObject(Stream s)
        {
            XmlSerializer ser = new XmlSerializer(typeof(List<List<NameValuePair>>));
            List<List<NameValuePair>> lst = (List<List<NameValuePair>>)ser.Deserialize(s);

            List<extrabutondata> extralist = new List<extrabutondata>();

            for (int i = 0; i < lst.Count; i++)
            {
                extrabutondata o = new extrabutondata();


                PropertyInfo[] properties = GetProperties(o);
                foreach (PropertyInfo pi in properties)
                {
                    NameValuePair nvp = FindInList(lst[i], pi.Name);
                    if (nvp != null)
                    {
                        pi.SetValue(o, nvp.PropertyValue, null);
                    }
                }

                extralist.Add(o);
            }

            return extralist;
        }
// and example type class it uses
    public class extrabutondata
    {
        public extrabutondata()
        {

        }
        public string name {get; set;}
        public int number { get; set;}
    }

Thanks again! that's another Daniweb form post solved by Sknake!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.