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

时间:2015-02-26 08:42:15

标签: c# xml-serialization

如果我有类似的话:

public abstract class Animal { }

[XmlRoot]
public class Dog:Animal { }

[XmlRoot]
Public class Cat:Animal { }

如何对像Cat对象进行序列化(然后能够反序列化):

<Cat> </Cat>

而不是像:

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

我尝试过使用System.Xml.Serialization和System.Runtime.Serialization命名空间中的不同组合,但我似乎无法得到它。

我可以通过指定序列化程序中对象的类型来获取序列化对象以查找我想要的方式。这适用于序列化,但不适用于反序列化。因为我不知道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 { }

1 个答案:

答案 0 :(得分:0)

我试了很长时间,也无法解决这个问题。我唯一想到的就是使用一些反射并自己生成文档:

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; }
}

要反序列化,你必须反过来做某种形式的工厂。

相关问题