-2

I have a XML document that several cases of specialization of entries, I managed to get a solution from here to deserialize it . I have done the exact same of the example and works as a charm, however It only works if a single specialized class appears. In the links example(shown above), imagine there is another class specialized from Instrument (for example, Drums). How can I use this method to tell the deserializar when to cast to each of the specialized classes?

Serialized object:

public class Orchestra
{
   public Instrument[] Instruments;
}   

public class Instrument
{
   public string Name;
}

public class Brass:Instrument
{
   public bool IsValved;
}

Example of deserialization:

 public void DeserializeObject(string filename)
   {
      XmlAttributeOverrides attrOverrides = 
         new XmlAttributeOverrides();
      XmlAttributes attrs = new XmlAttributes();

      // Create an XmlElementAttribute to override the Instrument.
      XmlElementAttribute attr = new XmlElementAttribute();
      attr.ElementName = "Brass";
      attr.Type = typeof(Brass);

      // Add the XmlElementAttribute to the collection of objects.
      attrs.XmlElements.Add(attr);

      attrOverrides.Add(typeof(Orchestra), "Instruments", attrs);

      // Create the XmlSerializer using the XmlAttributeOverrides.
      XmlSerializer s = 
      new XmlSerializer(typeof(Orchestra), attrOverrides);

      FileStream fs = new FileStream(filename, FileMode.Open);
      Orchestra band = (Orchestra) s.Deserialize(fs);
      Console.WriteLine("Brass:");

      /* The difference between deserializing the overridden 
      XML document and serializing it is this: To read the derived 
      object values, you must declare an object of the derived type 
      (Brass), and cast the Instrument instance to it. */
      Brass b;
      foreach(Instrument i in band.Instruments) 
      {
         b = (Brass)i;
         Console.WriteLine(
         b.Name + "\n" + 
         b.IsValved);
      }
   }

I have tried adding another attribute override but it does not work, I have also tried making two separate deserializers (each with an attribute override with the property I would like to cast to an object) it neither works as it fails to cast objects of the other type resulting in an type exception.

Any ideas?

  • If I remember correctly you can use `[XmlInclude]` for this. You'd have to explicitly include each derived class at compile time, though. – C.Evenhuis Aug 03 '15 at 18:00
  • If I interpreted it correctly [XmlInclude] works for serialization (I have used it to generate the xml file in fact). I have also indicated it for deserialization (I think is completeley unrelated). It would seem legit that what worked for serializing something will work backwards to deserialize it, but I couldn't manage. Therefore the deserialization example given by microsoft: – Javier Jimenez Aug 03 '15 at 18:15
  • [code] ... XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides(); XmlAttributes attrs = new XmlAttributes(); ...[code] XmlElementAttribute attr = new XmlElementAttribute(); attr.ElementName = "Brass"; attr.Type = typeof(Brass); attrs.XmlElements.Add(attr); attrOverrides.Add(typeof(Orchestra), "Instruments", attrs); XmlSerializer s = new XmlSerializer(typeof(Orchestra), attrOverrides); – Javier Jimenez Aug 03 '15 at 18:16
  • You need to look into generating xsd file from xml and then using xsd2code tool in Visual studio to generate appropriate code for you. This way all serialization logic will be taken care of for you. – Tanuki Aug 03 '15 at 18:16
  • @Tanuki I do not need code generation, I have the code. What I want is the instances which were written in the file. I can deserialize it all except when several specializations appear :( – Javier Jimenez Aug 03 '15 at 18:18

1 Answers1

0

If you have control of the .cs files you can add the attributes directly to the main class like this, serialization can be fully controlled using attributes, and in extreme cases you can implement the IXmlSerializable interface.

I've written a simple example here, but you should provide an example of what it is not working for you and the exception you are getting in order to see your concrete problem. There are many things that can fail while serializing and deserializing and the exception will tell you why. For example, if the class is not public it will throw an exception.

public class Program
{

    public class Orchestra
    {
        public Instrument[] Instruments {get; set;}
    }

    [XmlInclude(typeof(Brass))]
    [XmlInclude(typeof(Guitar))]
    public class Instrument
    {
        public string Name { get; set; }

        [XmlIgnore]
        public string DoNotSerialize { get; set; }
    }

    public class Brass : Instrument
    {
        public bool IsValved { get; set; }

        public override string ToString()
        {
            return string.Format("This is a{0} Brass.",IsValved?" valved":"n unvalved");
        }
    }
    public class Guitar: Instrument
    {
        public int Strings { get; set; }

        public override string ToString()
        {
            return String.Format("This is a {0} string Guitar.", Strings);
        }
    }

    static XmlSerializer s;

    static void Main(string[] args)
    {
        s = new XmlSerializer(typeof(Orchestra));
        serialize("%temp%test.xml");
        deserialize("%temp%test.xml");
        Console.ReadLine();
    }

    static void serialize(string filename)
    {
        Orchestra o = new Orchestra();
        o.Instruments = new Instrument[]
        {
            new Brass
            {
                IsValved=true
            },
            new Brass {IsValved=false },
            new Guitar {Strings=12 }
        };

        using (FileStream fs = new FileStream(filename, FileMode.Create))
        {
            s.Serialize(fs, o);
        }
    }

    static void deserialize(string filename)
    {
        Orchestra band;
        using (FileStream fs = new FileStream(filename, FileMode.Open))
        {
            band = (Orchestra)s.Deserialize(fs);
        }

        foreach (Instrument i in band.Instruments)
        {
            Console.WriteLine("{0}: {1}", i.GetType(), i.ToString());
        }
    }
}
jmservera
  • 6,454
  • 2
  • 32
  • 45
  • That is it! I do not have the slightest idea why this method did not work the first time I tried it, as I said I got a type casting exception (the object I was trying to deserialize could not be cast to one of the specializations). The error must be in another place of my code. Thank you for your help! – Javier Jimenez Aug 07 '15 at 09:51