4

We have the following class...

public class ValueHolder<T>
{
    public T Value { get; set; }
    public Type ValueType => typeof(T);
}

...which we of course can instantiate like this.

var foo = new ValueHolder<string>() { Value = "Hello World!" };
var laa = new ValueHolder<int>() { Value = 44 };

When we serialize foo and laa using NewtonSoft's Json.NET, we get the following output...

// foo
{
    "Value": "Hello World!",
    "ValueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
}

// laa
{
    "Value": 44,
    "ValueType": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
}

The problem is deserializing it since Json.NET doesn't know it's referring to a generic so it blows up. As such, I need to write a custom converter for this class. However, I'm not sure how to instantiate a new generic instance where the data type used for 'T' in ValueHolder is stored in string-form in ValueType.

More info

I'm actually trying to Serialize/Deserialize a subclass of Dictionary<string,ValueHolder<>> where the T of ValueHolder can be different for every instance (which of course you can't do so I'm actually subclassing Dictionary<string,object> then putting the resolved ValueHolder into 'object') so I think the proper approach is to put the converter on the dictionary and not on ValueHolder itself.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • It is unclear what you are asking.. You want to create a generic instance (of a class) (??) from it's type in string form.. ??? Makes no sense to me. Perhaps an example would clarify things. – Aniket Inge Aug 02 '15 at 07:46
  • See here: http://stackoverflow.com/a/1371756/261050 – Maarten Aug 02 '15 at 07:56
  • @Maarten, MakeGenericType looks interesting! Put that in an answer with an example and you'll get the credit. – Mark A. Donohoe Aug 02 '15 at 08:01

4 Answers4

0

Split you class to be like this:

public class ValueTypeBase
{
    public Type ValueType { get; set; }
}

public class ValueHolder<T> : ValueTypeBase
{
    public T Value { get; set; }
}

And check this code:

var json = "{\"Value\": \"Hello World!\",\"ValueType\": \"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\"}";
var valueTypeBase = JsonConvert.DeserializeObject<ValueTypeBase>(json);
var newType = typeof(ValueHolder<>).MakeGenericType(valueTypeBase.ValueType);
var obj = JsonConvert.DeserializeObject(json, newType);
Amir Popovich
  • 29,350
  • 9
  • 53
  • 99
  • I know that, but you just put the generic type's T value ('string' here) in the DeserializeObject call. That's the problem... ValueType represents the type used for T in ValueHolder. How can I specify the value for T based on the string representation of its type (which coincidentally here is also a string.) – Mark A. Donohoe Aug 02 '15 at 07:47
  • 1
    @Amir: the type 'string' is hardcoded. If I understand correctly the question is about make it dynamic, as it is not always string – g.pickardou Aug 02 '15 at 07:48
  • @MarqueIV the `json` string here, is the string-representation of `ValueHolder` so it would only make sense to deserialize to `ValueHolder` type. You cannot convert it to `ValueHolder` – Aniket Inge Aug 02 '15 at 07:49
  • @Aniket, again, you're missing the point. I'm not trying to convert it to ValueHolder, I'm trying to convert it to ValueHolder *in that instance* because ValueType says it's a string. But ValueType could be an int32 in which case I'd need to deserialize it to ValueHolder. That's what I'm asking... how do I create an instance of ValueType where T is stored as a string representation in ValueType. – Mark A. Donohoe Aug 02 '15 at 08:08
  • Hi Amir! That's very close to what I need. (The MakeGenericType I hadn't seen before. Very cool!) The issue is while that does give me the proper obj instance, I don't see how this can fit into their converter because it looks like you're calling DeserializeObject twice on the raw string whereas a converter uses a reader. I guess I could try caching the string first. Thoughts? Again, this is definitely the right path, but it's not quite there. (If it makes any difference, the actual object being serialized/deserialized is a dictionary of ValueHolder's) – Mark A. Donohoe Aug 02 '15 at 08:20
0

To create new objects from the assembly names, you can use:

Activator.CreateInstance() method. See the documentation here:

https://msdn.microsoft.com/en-us/library/system.activator.createinstance%28v=vs.110%29.aspx

Aniket Inge
  • 25,375
  • 5
  • 50
  • 78
  • I know about the activator. But that still doesn't answer how to activate a generic type. For instance if ValueType was System.Int, then the type I need to activate is ValueHolder. But I don't have 'int'. I have its string representation, nor do I want to activate it. Make sense? – Mark A. Donohoe Aug 02 '15 at 07:54
  • You have a string representation which is created using `ToString()`. Sounds like a bad way to do it – Aniket Inge Aug 02 '15 at 07:57
  • What are you trying to do? I understand your question now, but there could be a better way to solve this – Aniket Inge Aug 02 '15 at 07:58
  • Aniket, I'm sorry you're not understanding this. All the details are clearly in the question. No, I am not calling ToString as you say. That is what Json.NET is writing out because the type information needs to be written out as it's needed for deserialization. – Mark A. Donohoe Aug 02 '15 at 08:02
  • Aniket, I'm not sure what you are referring to 'a better way' when I haven't even suggested one yet. As I said, I have JSON that I know represents a ValueHolder class where T is stored as a string in ValueType. I need to deserialize that JSON into an instance of ValueHolder where T is a string, an int32, a long, whatever, depending on the type in ValueType. I'm not sure how to be any more clear. Again, sorry I can't be but your comments aren't making sense to me. – Mark A. Donohoe Aug 02 '15 at 08:16
0

hope this help.

Change ValueHolder class to this:

public class ValueHolder<T>
{
    public ValueHolder() { }
    public ValueHolder(T v)
    {
        Value = v;
    }
    public T Value { get; set; }
    public Type ValueType() { return typeof(T); }
}

then using this code:

        Type d1 = typeof(ValueHolder<>);
        //Your type -> "System.String"
        Type typeArgs = System.Type.GetType("System.String", false, true);
        Type constructed = d1.MakeGenericType(typeArgs);
        //Your Value-> "Test Value"
        var o = Activator.CreateInstance(constructed, "Test Value");    
Ali Amanzadegan
  • 174
  • 1
  • 5
0

Reconstructing the value using System.Type.GetType and Type.MakeGenericType is not a problem (kudos to Ali Amanzadegan and Maarten):

var foo = new ValueHolder<string>() { Value = "Hello World!" };
var laa = new ValueHolder<int>() { Value = 44 };
var daa = new ValueHolder<double> { Value = 123.999 };
//
var jsonFoo = JsonConvert.SerializeObject(foo);
var jsonLaa = JsonConvert.SerializeObject(laa);
var jsonDaa = JsonConvert.SerializeObject(daa);
//
Console.WriteLine(String.Format("{0}{3}{1}{3}{2}", jsonFoo, jsonLaa, jsonDaa, Environment.NewLine));
//
var o = JsonConvert.DeserializeObject<JObject>(jsonFoo);
var typeDescription = o.Properties().FirstOrDefault(p => p.Name == "ValueType");
var type = System.Type.GetType(typeDescription.Value.ToString());
var reFoo = (dynamic)JsonConvert.DeserializeObject(jsonFoo, typeof(ValueHolder<>).MakeGenericType(type));
Console.WriteLine(reFoo.Value);

The output:

Hello World!

The difficulty for me was to find a way to extract the functionality in a function/method. One possible solution that I came with was using dynamic again:

private dynamic reconstructValueHolderFromJson(string json)
{
    var o = JsonConvert.DeserializeObject<JObject>(json);
    var typeDescription = o.Properties().FirstOrDefault(p => p.Name == "ValueType");
    var type = System.Type.GetType(typeDescription.Value.ToString());
    return JsonConvert.DeserializeObject(json, typeof(ValueHolder<>).MakeGenericType(type));
}

Now reconstructing the object looks like this (with printing the value):

var reFoo = reconstructValueHolderFromJson(jsonFoo);
Console.WriteLine("{0}", reFoo.Value);

var reLaa = reconstructValueHolderFromJson(jsonLaa);
Console.WriteLine("{0}", reLaa.Value);

var reDaa = reconstructValueHolderFromJson(jsonDaa);
Console.WriteLine("{0}", reDaa.Value);

And the output:

Hello World!
44
123,999
keenthinker
  • 7,645
  • 2
  • 35
  • 45
  • Isn't your genericValueHolder redundant? You're just creating an instance of the type to go back and get its type. In other words you're not actually using the instance. If you remove the CreateInstance portion and start the line after the equals with typeof(...) then you'd have the type to pass right into DeserializeObject. – Mark A. Donohoe Aug 02 '15 at 14:52
  • Good point - you are correct. I have removed this part, because what stays is the same code as in the `reconstructValueHolderFromJson` method. Did you found a way to get an object of type `ValueHolder`? – keenthinker Aug 02 '15 at 15:45
  • Still working on it. I'm actually trying to serialize a dictionary> (which I'm really implementing as Dictionary) and I think the proper way is to put the converter on the dictionary itself. That way, then I don't need to cast it. I just stuff the result in the dictionary. Those using the dictionary know what type they need so they can take care of the cast. If I get it working, I'll post an update. This is definitely on the right path though. – Mark A. Donohoe Aug 02 '15 at 16:01