-->

How to serialize object with List inside in C# usi

2019-06-27 23:08发布

问题:

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.

回答1:

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>