Newtonsoft.Json: error on deserialize objects with generic fields - c#

I have a question: Does Json.NET correctly work with generics? I have the next code:
[TestClass]
public class TestClass1_Test
{
[TestMethod]
public void ToJson()
{
var mot = new TestClass1(1, "title");
var result = mot.ToJson();
Assert.IsNotNull(result);
var pobject = TestClass1.FromJson(result);
Assert.AreEqual(pobject.Id, mot.Id);
}
}
public class TestClass1
{
public TestClass1(int id, string name)
{
Id = new Field<int>(id);
Name = new Field<string>(name);
}
public Field<int> Id { get; set; }
public Field<string> Name { get; set; }
public string ToJson()
{
var jobject = JObject.FromObject(this);
return jobject.ToString();
}
public static TestClass1 FromJson(string json)
{
var obj = JObject.Parse(json).ToObject<TestClass1>();
return obj;
}
}
public class Field<T>
{
public Field(T val)
{
Value = default(T);
}
public T Value { get; set; }
}
But when I call var obj = JObject.Parse(json).ToObject<TestClass1>() I get next error:
Newtonsoft.Json.JsonReaderException: Error reading integer. Unexpected
token: StartObject. Path 'Id', line 2, position 10.
Where is my mistake? Or Json.NET does not work with generics?

Json.NET does indeed work with generics - I was able to serialize and deserialize one of your Field<int> objects just fine.
The error message I get with the above code (using Json.NET 4.5 r10) is:
Error reading integer. Unexpected token: StartObject. Path 'Id', line 2, position 10
where the stack trace implied it was trying to deserialize an integer when it ran into a {, which was the beginning of the Id object. I think this could well be a bug.
Yet this seems to work as expected when using Json.NET 3.5 r8. I did have to swap JsonConvert.DeserializeObject<TestClass1>(json) for JObject.Parse(json).ToObject<TestClass1>() as the latter isn't in this version.
The answer therefore is to try a different version of Json.NET.
There is also a bug in the Field constructor.
Value = default(T);
should be:
Value = val;

For reference; this error can also come about if you are deserializing an object that contains a nested JSON object as a string.
If you forget to stringify it, the parser throws up this error.

Related

Json.NET : Detect an absence of a property on Json which appears to be a member of my object

I'm trying to deserialize some Json objects using Json.NET. I'd like to be able to detect if I have a member in my class missing from the Json properties for that object. For instance, I have a class which looks like this :
public class MyClass
{
public int n;
public bool b;
public string s;
}
And a Json which looks like this
{"n":1,"b":true}
so it's missing the "s" property. When I try to deserialize that, members which are not in the Json will have default value. So "s" will be equal to null. Fair enough, but is it possible to detect that when I'm deserializing ?
In substance, I want to do pretty much the opposite of this Stackoverflow post
But in my case, the MissingMemberHandling setting seems to do nothing, sadly.
Json.Net provides a way to achieve that.
You can set an attribute on the property in your Model class. and if
that property is not available in JSON it'll throw an exception.
Here is the Example
Model
public class Videogame
{
[JsonProperty(Required = Required.Always)]
public string Name { get; set; }
[JsonProperty(Required = Required.AllowNull)]
public DateTime? ReleaseDate { get; set; }
}
Test Code
string json = #"{
'Name': 'Starcraft III'
}";
Videogame starcraft = JsonConvert.DeserializeObject<Videogame>(json);
You can read more about this here
This is what i can think of at the moment.
public void CheckData()
{
if(n == null)
{
Console.Write("n is null");
}
if (b == null)
{
Console.Write("b is null");
}
if (s == null)
{
Console.Write("s is null");
}
}
Run a method like that after json maps to the object to see if there were any still null.
Or you can try something like this
var fields = typeof(MyClass).GetFields();
foreach (var field in fields)
{
if(!json.ContainsKey(field.Name))
{
Console.Write($"{field.Name} is missing");
}
}

Deserialize string by class name

Let's say I have a Value that is deserialized from a class.
public class MyValue
{
public string MyPropertyA { get; set; }
public string MyPropertyB { get; set; }
public string DeserializationClass { get; } = typeof(MyValue).Name;
}
I serialize this using JsonConvert class. MyValue class has a property DeserializationClass that should be used as info from which class the string was serialized from. In other words, when I deserialize the string into an object, this property serves as info which class should be used to deserialize the string. However, I am kinda stuck here as I am not sure how to get back the class from the string. Can anybody help me here?
public class Program
{
void Main()
{
var serialized = Serialize();
var obj = Deserialize(serialized);
}
string Serialize()
{
var objValue = new MyValue { MyPropertyA="Something", MyPropertyB="SomethingElse" };
return JsonConvert.SerializeObject<MyClass>(value);
}
object Deserialize(string serialized)
{
//How to deserialize based on 'DeserializationClass' property in serialized string?
return = JsonConvert.Deserialize<???>(serialized);
}
}
EDIT: Modified example to make it more clear what I need as I don't have access to objValue when I need to deserialize the string.
probably you might need to use JsonSerializerSettings.
What you might need to do is
JsonSerializerSettings setting = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
};
and then while serializing use this setting.
var serialized = JsonConvert.SerializeObject(objValue,setting);
this will give you Json like this
{"$type":"WPFDatagrid.MyValue, WPFDatagrid","MyPropertyA":"Something","MyPropertyB":"SomethingElse","DeserializationClass":"MyValue"}
from this you can find the name of the class used it to actually get your type.
Hope this helps !!
There is an overload
If your Type is in form of a Namespace, you can obtain the type from a string representation:
Type objValueType = Type.GetType("Namespace.MyValue, MyAssembly");
object deserialized = JsonConvert.Deserialize(objValueType, serialized);

Generic Type JsonConvert.DeserializeObject<List<T>>(string)

I am using Newtonsoft.JSON. I won't know the type of object passed to this method, or retrieved to this method, so I am attempting to use DeserializeObject on an object I do not know the type of.
Is this possible? If so, how?
Here is my code.
public static List<T> GetObject<T>(string cacheKey, IEnumerable<T> obj)
{
using (HttpClient client = new HttpClient())
{
var response = client.GetAsync("http://localhost:53805/api/NonPersisted/Get/" + cacheKey).Result;
obj = JsonConvert.DeserializeObject<obj.GetType>(response.Content.ToString());
return obj.ToList();
}
}
I attempted first to use
obj = JsonConvert.DeserializeObject<List<T>>(response.Content.ToString());
This didn't work, obviously, it was unable to parse.
Getting the Type of the object won't build, it says obj is a variable but used like a type.
EDIT
It appears you can use a generic List<T> without knowing the type with JsonConvert.DeserializeObject<> The real error was that the response.Content was only returning the type. You need to have...
obj = JsonConvert.DeserializeObject<List<T>>(response.Content.ReadAsStringAsync().Result);
You can make GetObject method generic without having parameter IEnumerable<T> obj.
Following solution I am suggesting with assumption that you know the format of the JSON value being returned from the URL.
For example, that the URL returns JSON which contains array of items and each item has two properties firstName and lastName.
var response = "[{\"firstName\":\"Melanie\",\"lastName\":\"Acevedo\"},
{\"firstName\":\"Rich\",\"lastName\":\"Garrett\"},
{\"firstName\":\"Dominguez\",\"lastName\":\"Rose\"},
{\"firstName\":\"Louisa\",\"lastName\":\"Howell\"},
{\"firstName\":\"Stone\",\"lastName\":\"Bean\"},
{\"firstName\":\"Karen\",\"lastName\":\"Buckley\"}]";
I can write GetObject method as following.
public static List<T> GetObject<T>()
{
var response = "
[{\"firstName\":\"Melanie\",\"lastName\":\"Acevedo\"},
{\"firstName\":\"Rich\",\"lastName\":\"Garrett\"},
{\"firstName\":\"Dominguez\",\"lastName\":\"Rose\"},
{\"firstName\":\"Louisa\",\"lastName\":\"Howell\"},
{\"firstName\":\"Stone\",\"lastName\":\"Bean\"},
{\"firstName\":\"Karen\",\"lastName\":\"Buckley\"}]";
var obj = JsonConvert.DeserializeObject<List<T>>(response);
return obj.ToList();
}
Here T in above method can by any type which has properties firstName and lastName. For example
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public double Salary { get; set; }
}
I can call GetObject method by passing either Person or Employee and get the JSON string deserialize to the collection of objects of these classes as following.
var persons = GetObject<Person>();
foreach (var item in persons)
{
Console.WriteLine($"{item.FirstName} {item.LastName}");
}
var employees = GetObject<Employee>();
foreach (var item in employees)
{
Console.WriteLine($"{item.FirstName} {item.LastName}");
}
Overall, the point I am trying to make is if format of the JSON is know the passing appropriate Type to JsonConvert.Deserialize<T> should work without any problem.
If incoming JSON represents a collection and trying to deserialize it to a simple class would fail and vice versa too will not work.
So for your problem, if you know that JSON is going to be a collection then using JsonConvert.Deserialize<List<T>> should not give you any problem as long T has the properties to which values from JSON can be set.
I hope this would help you resolve your issue.
I don't think you can call Deserialize<T>(..) if you do not known type T.
Only thing I can think of is getting Object:
public static Object GetObject(string cacheKey)
{
using (HttpClient client = new HttpClient())
{
var response = client.GetAsync("http://localhost:53805/api/NonPersisted/Get/" + cacheKey).Result;
var obj = JsonConvert.DeserializeObject(response.Content.ToString());
return obj;
}
}
I am not in a position where I can test this right now, but I think the following could work. I have found that JSON.NET does not like List<T> when deserializing, I typically get around this by using arrays instead.
static HttpClient _httpClient = new HttpClient();
public static async Task<T[]> GetObject<T>(string cacheKey)
{
HttpResponseMessage response = await _httpClient .GetAsync("http://localhost:53805/api/NonPersisted/Get/" + cacheKey);
return JsonConvert.DeserializeObject<T[]>(response.Content.ToString());
}
I took the liberty of removing the using statement for HttpClient and made it a static member instead. I also changed the method to run async, you can change it back easily but as I mentioned in my comment you might want to avoid HttpClient and use WebClient instead.
You can do this by passing the type into the function without strongly typing it.
For example:
Type type = obj.GetType();
JsonConvert.DeserializeObject(json, type);

Cast generic parameter

I get an object back from a method. The object is of type object and I can't change this because of backwards compatibility. If it is of a certain type (Response<T> bellow) then I need to access the property Payload which is of type T so that I can serialize it as part of a another object and send it off. The problem is that since I don't know the type of T I can't cast the object to Response<T> to access Payload even though I don't care about its type.
Here is my object with the generic type:
public class Response
{
public int Status { get; set; }
public string Message { get; set; }
}
public class Response<T> : Response
{
public T Payload { get; set; }
}
Here is what I would like to do:
// Will sometimes be of type Response<T>
object data = LegacyCode();
if (data.GetType().IsGenericType && data.GetType().GetGenericTypeDefinition() == typeof(Response<>)) {
var payload = ((Response<object>)data).Payload; // Unable to cast object of type...
}
But the only way I could find to do this is by using dynamics.
// Will sometimes be of type Response<T>
object data = LegacyCode();
if (data.GetType().IsGenericType && data.GetType().GetGenericTypeDefinition() == typeof(Response<>)) {
var payload = ((dynamice)data).Payload;
}
Don't ask why things are the way they are (I'm wondering that myself). I have to do code gymnastics to keep backwards compatibility in this system. I just want compile time checking on the name of the property.
Here is a fiddle: https://dotnetfiddle.net/dXxHbD
UPDATE:
I need to be able to serialize and deserialize this object. Originally Response had a property Payload of type object. This caused serialization issues when Response<T> was deserialized because the Payload property was of type Newtonsoft.Json.Linq.JObject which could not be cast to T. Here is an example: https://dotnetfiddle.net/uc15HD
The problem was that I was going the wrong direction and the deserialization works if I cast T to object rather than try to cast object to T. When I store the value as its specific type T then the serializer knows what to deserialize the string to.
Here is an example using Jon's answer below: https://dotnetfiddle.net/KwudAx
Here is a similar example using Matias' solution of using covariance: https://dotnetfiddle.net/kCjZr4
To get compile-time checking of the name of the property, you can keep the dynamic typing, but get the runtime "mini-compiler" to do the hard work:
object data = LegacyCode();
object payload = GetPayload(data);
// Use payload
...
private static object GetPayload<T>(Response<T> response)
{
return response.Payload;
}
public static object GetPayload(object data)
{
// Fallback method. You could return null here and use
// that to indicate in the calling code that it wasn't a
// Response<T>...
}
A much better solution would be to add an non-generic interface or an extra base class though. For example:
public class Response
{
public int Status { get; set; }
public string Message { get; set; }
}
public interface IPayloadHolder
{
public object Payload { get; }
}
public class Response<T> : Response, IPayloadHolder
{
public T Payload { get; set; }
// By using explicit interface implementation, this
// doesn't get in the way for normal usage.
IPayloadHolder.Payload { get { return Payload; } }
}
Then you can use:
var payloadHolder = data as IPayloadHolder;
if (payloadHolder != null)
{
var payload = payloadHolder.Payload;
}
I believe that you need to use covariance.
Design an interface IResponse<out T>:
public interface IResponse<out T>
{
public T Payload { get; }
}
And implement it on Response<T>. Now you can cast it to IResponse<object>:
Response<string> x = new Response<string>();
IResponse<object> y = x; // implicit cast

Deserialize json string array to string array

Lets assume I have the following JSON Object:
{
"Key": "\"QTuY+0m31w2QiZGl4h+W8w==\"",
"Value":
[
"addgroup",
"viewgroup",
"editgroup"
]
}
How can I deserialize the Value part in a C# string[]?
The problem is, string[] is not the only type I have in the Value part, it can be everything, so I need to use dynamic.
I am using JSON.net and when I use the JsonConvert.DeserializeObject method I get a runtime binder exception.
This is the method I use to deserialize:
public async Task<Tuple<string, dynamic>> ReadCachingEntryAsync(string uri) {
var data = tools.ReadEntry(Path.Combine(cacheFolder, uri.Replace("/", "")));
var res = new Tuple<string, dynamic>(string.Empty, null);
if (data != null) {
var dat = JsonConvert.DeserializeObject<KeyValuePair<string, dynamic>>(UTF8Encoding.UTF8.GetString(data));
res = new Tuple<string, dynamic>(dat.Key, dat.Value);
}
return res;
}
The tools.ReadEntry(data) returns a byte[] containing the data, from the binary formatted file.
Here is the class KeyValuePair<K, V>
[Serializable]
internal struct KeyValuePair<K, V> {
public K Key { get; set; }
public V Value { get; set; }
}
You can check if it's a JArray and implement special handling. E.g.
byte[] data = Encoding.UTF8.GetBytes(#"{""Key"": ""Something"", ""Value"": [""test"", ""test 2"", ""test 3""]}");
var dat = JsonConvert.DeserializeObject<KeyValuePair<string, dynamic>>(UTF8Encoding.UTF8.GetString(data));
var value = dat.Value;
if (value is JArray)
{
var arrayType = value[0].Value.GetType().MakeArrayType();
value = value.ToObject(arrayType);
}
var res = new Tuple<string, dynamic>(dat.Key, value);
Just call DeserializeObject<T> replacing T with the type you intend to deserialize the input string into and json.NET will take care of the rest.
string[] json = JsonConvert.DeserializeObject<string[]>(inputString);
EDIT:
Ok, so assuming all of your json is in the form of the example posted in EDIT 3 then waht you really want is;
public class KVPair
{
public string Key { get; set; }
public string[] Value { get; set; }
}
KVPair pair = JsonConvert.DeserializeObject<KVPair>(UTF8Encoding.UTF8.GetString(data));
Now this is fairly static but I'm not sure you actually need dynamic. If you do then you'll want the Value property to be of type object or dynamic. However, if you go that route then you will unfortunately have to deal with this unknown type. I'm really not knowledgeable on the dynamic type so I can't comment on how to deal with it later on but if say for example, the array could contain, ints, floats or strings and you made Value of type object you would have inspect the array later on, determine the underlying type and cast each of the items to that type before you could do anything useful with them. Hope that helps. If you're lucky you'll just be able to use the code I posted above as it's far simpler and is actually type safe.

Categories

Resources