I am not sure if there's a better way to do this. maybe someone help?
I want to cast an object of type JObject
to a class in a factory. Class itself should be decided based on on another parameter. But I can only think of Serializing the object to a string an serializing back into a specific class. There has to be a better way?
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();
}
}
}
If you just supplied the right sort of input, you could cast them:
var voiceInput = new Input()
{
TypeId = 1,
ObjectDefinesInput = new VoiceInput(){ ... }
}
and
switch (input.TypeId)
{
case 1:
VoiceInput voiceInput = (VoiceInput)input.ObjectDefinesInput;
return new VoiceResponse(voiceInput);
default:
TextInput textInput = (textInput)input.ObjectDefinesInput;
return new TextResponse(textInput );
}
If you want some type safety, make your Input
class have a generic type argument for the type of input
public class Input<T>
{
public int TypeId { get; set; }
public T ObjectDefinesInput;
}
And
var voiceInput = new Input<VoiceInput>()
{
TypeId = 1,
ObjectDefinesInput = new VoiceInput(){ ... }
}
Then no casting required:
switch (input.TypeId)
{
case 1:
return new VoiceResponse(input.ObjectDefinesInput);
default:
return new TextResponse(input.ObjectDefinesInput);
}
Hmm, since I found out the type is actually JObject (as I deserialize into < object >, the underlying class is JObject), then I can do
input.ObjectDefinesInput.ToObject<TextInput>();
as per Converting a JToken (or string) to a given Type
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With