【问题标题】:using generics with xml deserialisation使用带有 xml 反序列化的泛型
【发布时间】:2012-10-30 09:41:21
【问题描述】:

我想将一个对象传递给 xml 反序列化的结果类型并保持强类型。

所以反序列化类可以采用任何实现 IResult 接口的类型,在这种情况下是 Result 和 Result2。

我通过使 getObject 方法返回动态来完成这项工作,但我更愿意保持编译时间检查,我认为这应该是可能的。

我尝试过使用泛型,如下例所示,但是 deser.getObject(doc());行给了我一个“不能从使用中推断”的编译错误。

感谢大家的帮助。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace SOQuestion
{
    class Program
    {
        static void Main(string[] args)
        {

            var deser = new Deserialised(new Result());
            var result = deser.getObject(doc());

            var deser2 = new Deserialised(new Result2());
            var result2 = deser.getObject(doc());

            Console.Writeline(result.status);
            Console.Writeline(result2.status);
        }

        public XmlDocument doc()
        {
            var doc = new XmlDocument();
            var el = (XmlElement)doc.AppendChild(doc.CreateElement("Result"));
            el.SetAttribute("status", "ok");
            el.SetAttribute("status2", "not ok");
            return doc;

        }

    }

    class Deserialised
    {

        private IResult result;
        private Type resultType;

        public Deserialised(IResult _result)
        {
            result = _result;
            resultType = Type.GetType(result.GetType().AssemblyQualifiedName);
        }


        public T getObject<T>(XmlDocument xml)
        {
            var mySerializer = new XmlSerializer(resultType);
            var myStream = new MemoryStream();
            xml.Save(myStream);
            myStream.Position = 0;
            var r = mySerializer.Deserialize(myStream);
            return (T)r;
        }

    }

    interface IResult
    {
        public string status {get;set;}
    }



    [Serializable]
    public class Result :IResult
    {
        [XmlAttribute]
        public string status { get; set; }
    }

    [Serializable]
    public class Result2 : IResult
    {
        [XmlAttribute]
        public string status2 { get; set; }
    }
}

【问题讨论】:

  • 您认为应该从哪里获得&lt;T&gt;?如果您手动操作,怎么知道T

标签: c# generics xml-serialization


【解决方案1】:

确实,这是行不通的——编译器无法从中知道T。请记住,T 来自编译时的调用者,而不是运行时方法的结果。 种方法可以在反射/泛型之间切换,但它很丑陋,在这里没有多大帮助。我只会返回object

public object GetObject(XmlDocument xml) {
    var mySerializer = new XmlSerializer(resultType);
    using(var myStream = new MemoryStream()) {
        xml.Save(myStream);
        myStream.Position = 0;
        return mySerializer.Deserialize(myStream);
    }
}

然后让调用者处理dynamic等:

var deser = new Deserialised(new Result());
dynamic result = deser.GetObject(doc());

var deser2 = new Deserialised(new Result2());
dynamic result2 = deser.GetObject(doc());

Console.Writeline(result.status);
Console.Writeline(result2.status);

由于上面的dynamic,两个Console.WriteLine中的.status仍然可以工作。

【讨论】:

  • 但这无济于事,因为我希望编译时检查调用方法。此外,反序列化的类在编译时确实知道类型,因为它被传递给构造函数,然后用于在 XmlSerialiser 中设置 resultType 变量。如果我将 Result 对象作为通用参数传递给 getObject 而不是从私有字段中获取它,它会改变什么吗?
  • @Jules 是的,这会给它一些推断 T 的东西,但是传入的实例(我怀疑)是完全多余的,因为序列化程序将创建实际的实例.
【解决方案2】:

在我看来,the answer you provided yourself 使事情变得过于复杂。 (除了做一些其他奇怪的事情,......见我的评论。)

在这里使用泛型没有任何好处。您也可以使用以下方法,您的两个要求仍然成立。

public IResult GetObject(XmlDocument xml)
{
    var mySerializer = new XmlSerializer(resultType);
    using (var myStream = new MemoryStream())
    {
        xml.Save(myStream);
        myStream.Position = 0;
        return (IResult)mySerializer.Deserialize(myStream);
    }
}

...简单地这样称呼它:

var deser = new Deserialised(new Result());
var result = (Result)deser.getObject(doc());

var deser2 = new Deserialised(new Result2());
var result2 = (Result2)deser.getObject(doc());

转换为任何不实现IResult 的东西都会触发编译器错误。


目前还不清楚您要在这里做什么,但假设您将Deserialised 设为通用。

class Deserialised<T>
    where T : IResult
{

    private T result;
    private Type resultType;

    public Deserialised(T _result)
    {
        result = _result;
    }


    public T getObject(XmlDocument xml)
    {
        var mySerializer = new XmlSerializer(typeof(T));
        var myStream = new MemoryStream();
        xml.Save(myStream);
        myStream.Position = 0;
        var r = (T)mySerializer.Deserialize(myStream);
        return r;
    }

}

您为什么还要将_result 作为参数传递并存储它?我的猜测是你只需要它,因为你不知道typeof()?在这种情况下,只需放下它。再次这样做后,您最终会得到一个定义泛型参数的类,其唯一目的是定义所需的演员表。

【讨论】:

  • 是的,当 Deserialised 类应该能够从构造函数参数中知道您想要的类型时,您必须强制转换结果似乎有点难看。有什么想法可以删除这个演员吗?
  • @Jules 稍后会有一些注意事项,但首先:演员阵容比指定泛型参数更丑陋的是什么?泛型为您做的唯一一件事就是在方法内部而不是外部进行强制转换。它本质上没有什么“通用”的,因此我发现使用通用方法一点也不清晰。这是相同数量的代码。
  • 您说得有道理,而且您似乎只想对 IResult 对象使用 Deserialised 类,因此您可以轻松地将 Deserialised 设为通用。我会更新我的答案。
  • 希望更新有意义。看起来你只是为了不做演员而跳过了很多不必要的漏洞。如果您知道强制转换不会失败,只需执行强制转换,您就可以从那里继续静态输入。
【解决方案3】:

所以我找到了一个解决方案,我的两个要求 1. 传入类型实现 IResult 接口 2. 返回的对象是静态类型的。 具有如下约束的通用方法

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace SOQuestion
{
    class Program
    {
        static void Main(string[] args)
        {

            var result = new Deserialised().getObject<Result>();

            var result2 = new Deserialised().getObject<Result2>();

            Console.WriteLine(result.status);
            Console.WriteLine(result.errorMessage);
            Console.ReadLine();


        }
    }

    class Deserialised
    {

        public T getObject<T>() where T : IResult
        {
            try
            {
                var instance = Activator.CreateInstance<T>();
                var mySerializer = new XmlSerializer(instance.GetType());
                var myStream = new MemoryStream();
                doc().Save(myStream);
                myStream.Position = 0;
                var r = mySerializer.Deserialize(myStream);
                throw new DivideByZeroException();
                return (T)r;
            }

            catch (Exception exp)
            {
                var instance = Activator.CreateInstance<T>();
                instance.errorMessage = "something wrong here";
                return instance;
            }
            ;
        }

    public static XmlDocument doc()
        {
            var doc = new XmlDocument();
            var el = (XmlElement)doc.AppendChild(doc.CreateElement("Result"));
            el.SetAttribute("status", "ok");
            el.SetAttribute("status2", "notok");
            return doc;

        }

    }

    interface IResult
    {
        string status { get; set; }
        string errorMessage { get; set; }
    }

    [Serializable]
    public class Result : IResult
    {
        [XmlAttribute]
        public string status { get; set; }

        [XmlAttribute]
        public string errorMessage { get; set; }

        [XmlAttribute]
        public string message { get; set; }
    }

    [Serializable]
    public class Result2 : IResult
    {
        [XmlAttribute]
        public string status { get; set; }

        [XmlAttribute]
        public string message2 { get; set; }

        [XmlAttribute]
        public string errorMessage { get; set; }
    }

    [Serializable]
    public class Result3
    {
        [XmlAttribute]
        public string status { get; set; }

        [XmlAttribute]
        public string message2 { get; set; }

        [XmlAttribute]
        public string errorMessage { get; set; }
    }
}

【讨论】:

  • 您使用Activator.CreateInstance&lt;T&gt;(); 只是为了调用GetType() 吗?只需使用typeof( T )
  • ...DivideByZeroException 是怎么回事?你总是会以这种方式返回“这里有问题”
  • 除以零是在测试 catch 块
猜你喜欢
  • 2018-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-18
  • 2013-08-26
相关资源
最近更新 更多