8

As you saw from the title, I need convert this object:

    object obj = new{
       Id = 1,
       Name = "Patrick"
    };

To specific class instance.

To be more clear, here is an example for you guys:

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }

    }

    public class Scholar
    {
        public int UniqueId { get; set; }
        public string FullName { get; set; }

    }

I have two classes Student and Scholar. I cant figure out a way to properly write an algorithm of conversion to specific type.

In my opinion, pseudo code should look like this :

if (obj.CanBeConverted<Student>()) {
   //should return this if statement

   obj = ConvertToType<Student>(o);

   // after this method obj type should change to Student
} else if (obj.CanBeConverted<Scholar>()) {

   //at current example wont reach this place
  obj = ConvertToType<Scholar>(o);

  // after this method obj type should change to Scholar
}

It this possible to program in some way ?


I surfed the net and found this example : https://stackoverflow.com/a/17322347/8607147

However this solution always tries to convert / deserialize object or dynamic type to concrete object.

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
Androidmuster
  • 107
  • 1
  • 2
  • 9
  • The first question I would have is: where does this anonymous object come from in the first place and can't you get that to return a concrete type instead? – DavidG Apr 25 '18 at 12:06
  • what about `new Student(obj.Id, obj.Name)` ? Or what is it that I am completely missing here? – pijemcolu Apr 25 '18 at 12:10
  • @DavidG this object comes from frond-end to API. I would like to pass dynamic type object to API method convert to concrete type , and then do some work with it. – Androidmuster Apr 25 '18 at 12:12
  • OK, that still doesn't answer my question. Why does the code talking to the API give you an anonymous type? I do lots of API work and never worry about this. – DavidG Apr 25 '18 at 12:13
  • Even if it's anonymous, obj will have a specific type generated by the compiler ant not available at source code level. You can only write a conversion method which will build a new object of the required type, and assign to it properties from the anonymous type, but you can't cast the anonymous type to a type from your hierarchy. – oviuan Apr 25 '18 at 12:14
  • @Androidmuster : - How would you differentiate the object passed from front to API whether it's `Student` or `Scholar`? I think you want to pass an object and based on the requirement, you will convert it to related class. – DhavalR Apr 25 '18 at 12:17
  • Imagine you have 20 classes and want with only **one** API method save their data to database. Also, you dont want that front-end would know something about that entity(for example `Student or Scholar`). It is a good practice to make separate modules which knows as less as possible it can. Of course, maybe Im a dreamer and this type of logic cannot be implemented. – Androidmuster Apr 25 '18 at 12:18
  • It is possible or not; you need to consider that when you pass object from front; number of properties and type of properties must be same. As well; the meaning of it too. – DhavalR Apr 25 '18 at 12:22
  • Then you need to look at generics, but without some idea of what your "one API method" looks like, we can't help. – DavidG Apr 25 '18 at 12:22
  • 2
    There are two problems here to address, first like @Androidmuster says, you must be able to determine which of your types is an appropriate container for the data in the dynamic object (based solely on what was provided in the question I don't see a reliable way). Then, you can override the explicit cast operator on your classes to allow explicit cast from object to a concrete instantiation of your classes (Google it, there are many examples on the interwebs). – Kevin Apr 25 '18 at 12:23
  • I appreciate your help guys. Every comment is a useful for me. @Kevin maybe you can provide me with a good/correct "example on the interweb". – Androidmuster Apr 25 '18 at 12:33
  • See the examples here https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/using-conversion-operators – Kevin Apr 25 '18 at 16:28

1 Answers1

10

You can do it by using Json.Net Schema and Json.Net, check bellow how I did it:

  class Program
  {
    static void Main(string[] args)
    {
      var o = new
      {
        Id = 1,
        Name = "Patrick",
        Courses = new[] { new { Id = 1, Name = "C#" } }
      };

      Student student = null;
      Scholar scholar = null;

      if (o.CanBeConverted<Student>())
        student = o.ConvertToType<Student>();
      else if (o.CanBeConverted<Scholar>())
        scholar = o.ConvertToType<Scholar>();

      System.Console.WriteLine(student?.ToString());
      System.Console.WriteLine(scholar?.ToString());

      System.Console.ReadKey();
    }
  }

  public static class ObjectExtensions
  {
    public static bool CanBeConverted<T>(this object value) where T : class
    {
      var jsonData = JsonConvert.SerializeObject(value);
      var generator = new JSchemaGenerator();
      var parsedSchema = generator.Generate(typeof(T));
      var jObject = JObject.Parse(jsonData);

      return jObject.IsValid(parsedSchema);
    }

    public static T ConvertToType<T>(this object value) where T : class
    {
      var jsonData = JsonConvert.SerializeObject(value);
      return JsonConvert.DeserializeObject<T>(jsonData);
    }
  }

  public class Student
  {
    public int Id { get; set; }
    public string Name { get; set; }
    public Courses[] Courses { get; set; }

    public override string ToString()
    {
      return $"{Id} - {Name} - Courses: {(Courses != null ? String.Join(",", Courses.Select(a => a.ToString())) : String.Empty)}";
    }
  }

  public class Courses
  {
    public int Id { get; set; }
    public string Name { get; set; }

    public override string ToString()
    {
      return $"{Id} - {Name}";
    }
  }

  public class Scholar
  {
    public int UniqueId { get; set; }
    public string FullName { get; set; }

    public override string ToString()
    {
      return $"{UniqueId} - {FullName}";
    }
  }

The solution is basically by generating a JSON Schema from your desired object and check if the new data que fit this shema.

Rodrigo
  • 1,488
  • 1
  • 10
  • 8