4

I have object with members of different types like this:

public class MyObject
{
    public string Str1 = string.Empty;
    public MyEnums.Enum1 E1 = MyEnums.Enum1.Unknown;
    public bool Done = false;
};

I have Dictionary of these objects:

Dictionary<string, MyObject> MyObjectsDic = new Dictionary<string, MyObject>();

And serializer for it like this:

public static void ToXml(string file, string collectionName, Dictionary<string, object> collection)
{
    XElement root = new XElement(collectionName);

    root.Add(collection.Select(x => new XElement("Item", new XAttribute("Object", x.Key),
            x.Value.GetType().GetFields().Select(f => new XElement(f.Name, f.GetValue(x.Value))))));

    root.Save(file);
}

The serializer tooks abstract Dictionary as argument and I need to convert my MyObjectsDic manually. May be I mistaken here.

ToXml("MyFile.xml", "MyObjects", MyObjectsDic.ToDictionary(p => p.Key, p => (object)p.Value));

I used this advice to make the serializer. It works good, but I need to add to MyObject new member

List<MyEnums.Enum2> Codes = new List<MyEnums.Enum2>();

And store some values here

var a = new MyObject {...};
a.Codes.Add(MyEnums.Enum2.Code1);
a.Codes.Add(MyEnums.Enum2.Code2);
MyObjectsDic.Add("Obj1", a);

But these list is serialized into file like

<Codes>Code1Code2<Codes/>

Without any space or delimiter. And I don't know how to make it more readable without modifications in serializer and without adding new odd code. The only idea I got is to keep already prepared string in MyObject instead of List<...>. It isn't graceful, but simple and works. I don't read these data, just only write and save as log into file.
Or I need to change my so cool serializer?

Upd.

I used the solution below, but I'm receiving an exception on Windows XP. On other OS's it works good. I modified code to make it like helper not an class extension.

Exception during dumping MyObjectsDic: There was an error reflecting type 'MyObject'.  
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportElement(TypeModel model, XmlRootAttribute root, String defaultNamespace, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type, XmlRootAttribute root, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type)
at MyXmlSerializerHelper.SerializeToXElement[T](T obj, XmlSerializer serializer, Boolean omitStandardNamespaces) in MyXmlSerializerHelper.cs:line 16
at MyXmlSerializerHelper.  <SerializeToFile>b__0[T](KeyValuePair'2 x) in MyXmlSerializerHelper.cs:line 5

The only idea I have - different versions of framework or some other religious issues on XP... Unfortunately, I can't install any other software or .Net version in production.

piet.t
  • 11,718
  • 21
  • 43
  • 52
Jury
  • 1,227
  • 3
  • 17
  • 30

1 Answers1

3

Rather than attempting to use reflection to manually serialize your MyObject class, you can use XmlSerializer to serialize your dictionary values directly to an XElement using the following methods, then include the result in the element tree you are building:

public static class XObjectExtensions
{
    public static XElement SerializeToXElement<T>(this IDictionary<string, T> collection, string collectionName)
    {
        return new XElement(collectionName, collection.Select(x => new XElement("Item", new XAttribute("Object", x.Key), x.Value.SerializeToXElement().Elements())));
    }

    public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer = null, bool omitStandardNamespaces = true)
    {
        var doc = new XDocument();
        using (var writer = doc.CreateWriter())
        {
            XmlSerializerNamespaces ns = null;
            if (omitStandardNamespaces)
                (ns = new XmlSerializerNamespaces()).Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
            (serializer ?? new XmlSerializer(obj.GetType())).Serialize(writer, obj, ns);
        }
        var element = doc.Root;
        if (element != null)
            element.Remove();
        return element;
    }
}

This automatically causes all fields and properties of MyObject to be serialized properly. Using this, the resulting XML will look like:

<MyObjects>
  <Item Object="Obj1">
    <Str1>Test object</Str1>
    <E1>Unknown</E1>
    <Done>false</Done>
    <Codes>
      <Enum2>Code1</Enum2>
      <Enum2>Code2</Enum2>
    </Codes>
  </Item>
</MyObjects>
dbc
  • 104,963
  • 20
  • 228
  • 340
  • 1
    It works! Thanks a lot! I can't understand a magic is going there, and difference, but it works. – Jury Jun 09 '15 at 00:03
  • I got an exception on.... Windows XP. On other systems works good. I've updated my question. – Jury Jul 02 '15 at 14:30
  • @Jury - 1) Was there an inner exception or additional message? 2) Try installing any patches or updates on that XP machine. The latest version of .Net for XP is version 4 which should be fine. 3) Can you share the class which is causing the exception? – dbc Jul 02 '15 at 16:12
  • 1) It was inner exception 2) I can't install anything in these machines 3) Unfortunately, I don't have yet access to this code. I made so: on XP is used serializer as I posted in question, on other OS's - your solution. Percentage of XP is small, so it is not so critical, but, yep, not graceful. – Jury Jul 23 '15 at 15:25