【问题标题】:Is there a better way to cast an JObject to any implementation of an interface? [duplicate]有没有更好的方法将 JObject 转换为接口的任何实现? [复制]
【发布时间】:2017-09-01 15:35:35
【问题描述】:

我不确定是否有更好的方法来做到这一点。也许有人帮忙?

我想将JObject 类型的对象转换为工厂中的一个类。类本身应该根据另一个参数来决定。但我只能想到将对象序列化为字符串,然后再序列化回特定的类。一定有更好的方法吗?

https://dotnetfiddle.net/3Qwq6V

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;

namespace Test
{
    public class Input
    {
        public int TypeId { get; set; }
        public object ObjectDefinesInput;
    }

    public class VoiceInput
    {
        public string Language;
    }

    public class TextInput
    {
        public string Encoding;
    }

    public interface IResponse
    {
        void Respond();
    }

    public class VoiceResponse : IResponse
    {
        private VoiceInput input { get; set; }
        public VoiceResponse(VoiceInput input) { this.input = input; }

        public void Respond()
        {
            // use information on VoiceInput to do something
            Console.WriteLine("(In "+ this.input.Language +"): beep buup boop.");
        }
    }

    public class TextResponse : IResponse
    {
        private TextInput input { get; set; }
        public TextResponse(TextInput input) { this.input = input; }
        public void Respond()
        {
            Console.WriteLine("I am a text handler. Using "+ this.input.Encoding +".");
        }
    }

    public static class ResponseFactory
    {
        public static IResponse CreateResponseHandler(Input input)
        {
            // ----------------- ISSUE HERE -----------------------------//
            // I'm using JsonConvert to serialize an <object> to a string, and then 

            string jsonObjectDefinesInput = JsonConvert.SerializeObject(input.ObjectDefinesInput, new JsonSerializerSettings
            {
                Formatting = Formatting.Indented,
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            });

            switch (input.TypeId)
            {
                case 1:
                    // (VoiceInput) input.ObjectDefinesInput throws exception
                    // input.ObjectDefinesInput as VoiceInput returns null
                    VoiceInput voiceInput = JsonConvert.DeserializeObject<VoiceInput>(jsonObjectDefinesInput);
                    return new VoiceResponse(voiceInput);
                default:
                    TextInput textInput = JsonConvert.DeserializeObject<TextInput>(jsonObjectDefinesInput);
                    return new TextResponse(textInput);
            }
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            string jsonData1 = "{ \"typeId\": 1, \"ObjectDefinesInput\": { \"Language\": \"Robot\" } }";
            string jsonData2 = "{ \"typeId\": 2, \"ObjectDefinesInput\": { \"Encoding\": \"UTF-8\" } }";

            Input someInpput1 = JsonConvert.DeserializeObject<Input>(jsonData1);
            Input someInpput2 = JsonConvert.DeserializeObject<Input>(jsonData2);

            IResponse testResponse1 = ResponseFactory.CreateResponseHandler(someInpput1);
            IResponse testResponse2 = ResponseFactory.CreateResponseHandler(someInpput2);

            testResponse1.Respond();

            testResponse2.Respond();

            Console.ReadLine();
        }
    }
}

【问题讨论】:

  • 请提供此问题中的代码,因此如果有人在 3 年内查看您的问题,也会看到属于它的代码。外部资源可能会被多次删除。
  • @TobiasTheel 好点。完成。
  • 任意对象o的运行时类型可以通过调用o.GetType()来确定。
  • 你为什么在CreateResponseHandler中序列化和反序列化input?那里的目标是什么?你从input.GetType() 得到什么?你是说你只想要case 1: return new VoiceResponse((VoiceInput)input);
  • 我猜测了一下,因为我对 input.ObjectDefinesInput 是什么只有一个微不足道的推断。

标签: c#


【解决方案1】:

如果你只是提供了正确的输入,你可以转换它们:

var voiceInput = new Input()
{
    TypeId = 1,
    ObjectDefinesInput = new VoiceInput(){ ... }
}

switch (input.TypeId)
{
    case 1:
          VoiceInput voiceInput = (VoiceInput)input.ObjectDefinesInput;
          return new VoiceResponse(voiceInput);
    default:
          TextInput textInput = (textInput)input.ObjectDefinesInput;
          return new TextResponse(textInput );
}

如果您想要某种类型安全,请让您的 Input 类具有输入类型的泛型类型参数

public class Input<T>
{
    public int TypeId { get; set; }
    public T ObjectDefinesInput;
}

还有

var voiceInput = new Input<VoiceInput>()
{
    TypeId = 1,
    ObjectDefinesInput = new VoiceInput(){ ... }
}

那么不需要强制转换:

switch (input.TypeId)
{
    case 1:
          return new VoiceResponse(input.ObjectDefinesInput);
    default:
          return new TextResponse(input.ObjectDefinesInput);
}

【讨论】:

  • 在打开 input.TypeId 之前,我不知道应该将 ObjectDefinesInput 转换为哪个类。
  • @EvaldasRaisutis 确实如此。我认为这就是问题的重点。
  • 我不能做新的 Input,因为 Input 是从 JSON 反序列化的。
  • @EvaldasRaisutis 你的问题说你只是在序列化和反序列化,因为你不知道如何转换。我认为问题是如何这样做。我的错。
【解决方案2】:

嗯,因为我发现类型其实是JObject(我反序列化成,底层是JObject),那我就可以了

input.ObjectDefinesInput.ToObject<TextInput>();

根据Converting a JToken (or string) to a given Type

【讨论】:

  • 我终于将您的代码粘贴到 VS 中,并通过我的头脑了解真正的问题是什么。这是一个我不知道的好功能。 +1。我是否可以建议将您的问题重新命名为“如何将 JObject 转换为已序列化的实际类型”?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-27
  • 1970-01-01
  • 2022-09-23
  • 2014-12-10
  • 1970-01-01
  • 2015-03-28
  • 1970-01-01
相关资源
最近更新 更多