序列化子类时如何控制元素名称



如果我有类似的东西:

public abstract class Animal { }
[XmlRoot]
public class Dog:Animal { }
[XmlRoot]
Public class Cat:Animal { }

我该如何序列化(然后能够反序列化)一个Cat对象,比如:

<Cat> </Cat>

而不是像

<Animal type="Cat"> </Animal>

我试过使用系统中的不同组合。Xml。序列化和系统。运行时。序列化名称空间,但我似乎无法获得它。

通过在序列化程序中指定对象的类型,我可以使序列化的对象看起来像我想要的样子。这适用于序列化,但不适用于反序列化。。因为我不知道xml中对象的类型。


一个可能的解决方案是:

public abstract class Animal 
{ 
    static Dictionary<String,Type> typeDic;
    static Animal()
    {
        typeDic = new Dictionary<string, Type>();
        //Get classes in the same namespace of this object
        Type ctype = typeof(Animal);
        Type[] types = ctype.Assembly.GetTypes().Where(t => String.Equals(t.Namespace, ctype.Namespace, StringComparison.Ordinal)).ToArray();
        //For any XmlRootAttribute objects, add the ElementName and Type to a dictionary
        foreach(Type type in types)
        {
            foreach (XmlRootAttribute xmlRoot in type.GetCustomAttributes(typeof(XmlRootAttribute), false))
            {
                typeDic.Add(xmlRoot.ElementName, type);
            }
        }
    }
    public static Content read(String XML)
    {
        XmlSerializer s = null;
        //check the first element to see what the name is, then create the serializer using the type from the dictionary
        XmlReader reader = XmlReader.Create(GenerateStreamFromString(XML));
        reader.Read();
        if (reader.Name == "xml")
        {
            while (reader.MoveToContent() != XmlNodeType.Element) { }
        }
        if (typeDic.ContainsKey(reader.Name))
            s = new XmlSerializer(typeDic[reader.Name]);
        else
            throw new Exception("Unknown Type in read");
        return (Content)s.Deserialize(reader);
    }
    public static string write<T>(T f)
    {
        XmlSerializer s = new XmlSerializer(typeof(T));
        MemoryStream stream = new MemoryStream();
        s.Serialize(stream, f);
        stream.Position = 0;
        return StreamToString(stream);
    }
}
[XmlRoot(ElementName="Dog")]
public class Dog:Animal { }
[XmlRoot(ElementName="Cat")]
Public class Cat:Animal { }

我试了很长时间,也没能弄清楚。我唯一能想到的就是使用一些反射并自己生成文档:

class Program
{
    static void Main(string[] args)
    {
        List<Animal> animals = new List<Animal>
        {
            new Dog{Name = "Ernie", HasFleas = true},
            new Cat{ Name = "Bert", Collar = "Blue with a little bell" }
        };
        XDocument doc = new XDocument();
        doc.Declaration = new XDeclaration("1.0","utf-8","true");
        doc.Add(new XElement("root", animals.Select(animal => animal.GetElement)));
        Console.WriteLine(doc);

    }
}

public abstract class Animal 
{
    [XmlAttribute]
    public string Name { get; set; }
    public XElement GetElement 
    {
        get 
        {
            Type type = this.GetType();
            XElement result = new XElement(type.Name);
            foreach (PropertyInfo property in
                type.GetProperties().Where(pi=> pi.CustomAttributes.Any(ca=> ca.AttributeType == typeof(System.Xml.Serialization.XmlAttributeAttribute))))
            {
                result.Add(new XAttribute(property.Name,property.GetValue(this)));
            }
            foreach (PropertyInfo property in
                type.GetProperties().Where(pi => pi.CustomAttributes.Any(ca => ca.AttributeType == typeof(System.Xml.Serialization.XmlElementAttribute))))
            {
                result.Add(new XElement(property.Name,property.GetValue(this)));
            }
            return result;
        }
    }
}
public class Dog : Animal 
{ 
    [XmlAttribute]
    public bool HasFleas { get; set; }
}
public class Cat : Animal 
{
    [XmlElement]
    public string Collar { get; set; }
}

要反序列化,您必须以另一种方式进行,因此需要某种形式的工厂。

最新更新