简体   繁体   English

从接口序列化通用类时,如何让DataContractJsonSerializer在类型提示中使用具体类型

[英]How do I get DataContractJsonSerializer to use concrete type in type hint when serializing generic class from interface

I have a set of classes as follows: a Command, which Executes and stores a Result; 我有一组如下的类:一个命令,执行并存储一个结果; a Response, which is created as in order to return the Result in a serialized form (plus extra metadata which I've left out). 响应,其创建目的是为了以序列化形式返回结果(加上我遗漏的其他元数据)。 The Response.Result must be of type object, as it is used for a bunch of different commands, each of which can have a Result of any type at all. Response.Result 必须为object类型,因为它用于一堆不同的命令,每个命令都可以具有任何类型的Result。

The Command is generic, and I'd like it to accept an interface rather than concrete type, but when I do, the serialized response contains the following type hint: 该命令是通用的,我希望它接受接口而不是具体类型,但是当我这样做时,序列化响应包含以下类型提示:

"__type":"ResultOfanyType:#serialization"

rather than the following, which is generated when the command accepts a concrete type: 而不是以下命令,它是在命令接受具体类型时生成的:

"__type":"ResultOfMyObjectDhOQ6IBI:#serialization"

I need the type hint to contain the concrete type rather than ResultOfanyType. 我需要类型提示来包含具体类型而不是ResultOfanyType。 Why are interfaces being treated differently in this context? 为什么在这种情况下对接口进行不同的对待? Notice that when the Type is a direct property of the serialized Command, then the concrete type is contained in the type hint 请注意,当Type是序列化Command的直接属性时,则具体类型包含在类型提示中

I've tried changing the the Result's Response property typed to Result, but that has no effect. 我尝试将类型为Result的Result的Response属性更改为Result,但这无效。

Here is the code. 这是代码。 Simply uncomment/comment the lines in Main where the command is created and known types listed for the alternative version. 只需取消注释/注释Main中创建命令的行,并列出替代版本的已知类型。

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

namespace serialization
{
    class Program
    {
        static void Main(string[] args)
        {
            Response response = new Response();
            response.ResponseStatus = "ok";
            ConcreteCommand command = new ConcreteCommand();    //switch with line below to test inteface
            //InterfaceCommand command = new InterfaceCommand();
            command.Execute();
            response.Results = command.Results;
            List<Type> knownTypes = new List<Type>
            {
            typeof(Result<MyObject>),                  //switch with Interface lines below to test inteface
            typeof(MyObject)
            //typeof(Result<IMyObject>),
            //typeof(IMyObject)
            };
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(response.GetType(), knownTypes, int.MaxValue, false, null, true);
            Stream stream = new MemoryStream();
            serializer.WriteObject(stream, response);
            stream.Position = 0;
            StreamReader reader = new StreamReader(stream);
            string output = reader.ReadToEnd();
            Console.WriteLine(output);
        }
    }

    public interface IMyObject
    {
        string name { get; set; }
    }

    [DataContract]
    [KnownType(typeof(MyObject))]
    public class MyObject : IMyObject
    {
        [DataMember]
        public string name { get; set; }
    }

    [DataContract]
    public class Result<T>
    {
        [DataMember]
        public string Status { get; set; }

        [DataMember]
        public T Item { get; set; }
    }

    public abstract class BaseCommand<T>
    {
        protected Result<T> results = new Result<T>();

        protected T resultObject;

        public object Results
        {
            get { return this.results; }
        }

        public T ResultObject
        {
            get { return this.resultObject; }
        }

        public abstract void Execute();
    }

    public class InterfaceCommand : BaseCommand<IMyObject>
    {
        public override void Execute()
        {
            IMyObject myobject = new MyObject();
            myobject.name = "my object";
            Result<IMyObject> result = new Result<IMyObject>();
            result.Item = myobject;
            result.Status = "ok";
            this.results= result;
            this.resultObject = myobject;
        }
    }

    public class ConcreteCommand : BaseCommand<MyObject>
    {
        public override void Execute()
        {
            MyObject myobject = new MyObject();
            myobject.name = "my object";
            Result<MyObject> result = new Result<MyObject>();
            result.Item = myobject;
            result.Status = "ok";
            this.results = result;
            this.resultObject = myobject;
        }
    }

    [DataContract]
    public class Response
    {
        [DataMember]
        public string ResponseStatus { get; set; }

        [DataMember]
        public object Results { get; set; }
    }
}

Let's start with this question and that might explain everything. 让我们从这个问题开始,它可以解释一切。

I need the type hint to contain the concrete type rather than ResultOfanyType. 我需要类型提示来包含具体类型而不是ResultOfanyType。 Why are interfaces being treated differently in this context? 为什么在这种情况下对接口进行不同的对待?

An interface is basically just a contract for what a class implementing it should contain and multiple classes could implement its members. 一个接口基本上只是一个实现它的类应包含的协定,而多个类可以实现其成员。 For example. 例如。

public interface IPerson
{
    int Id { get; set; }
    string FirstName { get; set; }
    string LastName { get; set; }
}

public class Person : IPerson
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public DateTime DateOfBirth { get; set; }
}

public class Contact : IPerson
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string Company { get; set; }
    public string PhoneNumber { get; set; }
}

So when you call an IPerson what are you expecting back? 因此,当您致电IPerson ,您会期待什么? A Person or a Contact ? 一个Person还是一个Contact Each has an id and the basic components of a name, but each also has unique properties that IPerson doesn't even know exist. 每个都有一个ID和一个名称的基本组成部分,但每个都有IPerson甚至不知道存在的独特属性。 This is why when you try to get an interface to resolve to a concrete class, you're not going to get anywhere without some sort of factory class to figure out what you want. 这就是为什么当您尝试获取一个接口来解析为具体的类时,如果没有某种工厂类来弄清楚您想要的东西,您将无所适从。 So in this case, if I wanted to resolve an IPerson , I'd add the following line of code... 因此,在这种情况下,如果要解析IPerson ,请添加以下代码行...

var objectType = iPersonObject.GetType();

In your case, you'd want to try calling GetType() on result.Item . 对于您的情况,您想尝试在result.Item上调用GetType() This tells .NET to look at the actual type of the object implementing the interface and return it. 这告诉.NET查看实现接口的对象的实际类型并返回它。

How about this... 这个怎么样...

class Program
{
    static void Main(string[] args)
    {
        Response response = new Response();
        response.ResponseStatus = "ok";
        //ConcreteCommand command = new ConcreteCommand();    //switch with line below to test inteface
        InterfaceCommand command = new InterfaceCommand();
        command.Execute();
        response.Results = command.Results;
        List<Type> knownTypes = new List<Type>
        {
            typeof(MyObject),
            typeof(Result<MyObject>)                  //switch with line below to test inteface
            //typeof(Result<IMyObject>)
        };
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(response.GetType(), knownTypes, int.MaxValue, false, null, true);
        Stream stream = new MemoryStream();
        serializer.WriteObject(stream, response);
        stream.Position = 0;
        StreamReader reader = new StreamReader(stream);
        string output = reader.ReadToEnd();
        Console.WriteLine(output);
    }
}

public interface IMyObject
{
    string name { get; set; }
}

[DataContract]
public class MyObject : IMyObject
{
    [DataMember]
    public string name { get; set; }
}

[DataContract]
public class Result<T>
{
    [DataMember]
    public string Status { get; set; }

    [DataMember]
    public T Item { get; set; }
}

public abstract class BaseCommand
{
    protected Result<IMyObject> results = new Result<IMyObject>();

    public Result<IMyObject> Results
    {
        get { return this.results; }
    }

    public abstract void Execute();
}

public class InterfaceCommand : BaseCommand
{
    public override void Execute()
    {
        IMyObject myobject = new MyObject();
        myobject.name = "my object";
        Result<IMyObject> result = new Result<IMyObject>();
        result.Item = myobject;
        result.Status = "ok";
        this.results= result;
    }
}

public class ConcreteCommand : BaseCommand
{
    public override void Execute()
    {
        MyObject myobject = new MyObject();
        myobject.name = "my object";
        Result<IMyObject> result = new Result<IMyObject>();
        result.Item = myobject;
        result.Status = "ok";
        this.results = result;
    }
}

[DataContract]
public class Response
{
    [DataMember]
    public string ResponseStatus { get; set; }

    [DataMember]
    public Result<IMyObject> Results { get; set; }
}

Outputs... 输出...

{"__type":"Response:#ConsoleApplication2","ResponseStatus":"ok","Results":{"__ty
pe":"ResultOfanyType:#ConsoleApplication2","Item":{"__type":"MyObject:#ConsoleAp
plication2","name":"my object"},"Status":"ok"}}

If you're trying to make some sort of generic contract, you're going to have to have some sort of common base class/interface. 如果您要制定某种通用合同,则必须具有某种通用的基类/接口。 It won't work with object but you can go ala COM and make your own IUnknown interface from which to create as many subclasses as you like, as long as they are included within your known types. 它不适用于对象,但是您可以在COM上创建自己的IUnknown接口,从中创建任意数量的子类,只要它们包含在已知类型中即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM