Deserializing a string property value to a class instance using JSON.Net - c#

I'm deserializing some JSON from a server which is, for the most part, simple:
{
"id": "ABC123"
"number" 1234,
"configured_perspective": "ComplexPerspective[WithOptions,Encoded]"
}
That "configured_perspective" property, however, is an unfortunate case of the server using a weirdly put-together string when a nested object would have been better.
To ease the suffering of our .NET users, I convert this into a custom class in my object model:
public class Example
{
public string id { get; set; }
public int number { get; set; }
public Perspective configured_perspective { get; set; }
}
// Note, instances of this class are immutable
public class Perspective
{
public CoreEnum base_perspective { get; }
public IEnumerable<OptionEnum> options { get; }
public Perspective(CoreEnum baseArg, IEnumerable<OptionEnum> options) { ... }
public Perspective(string stringRepresentation) {
//Parses that gross string to this nice class
}
public static implicit operator Perspective(string fromString) =>
new Perspective(fromString);
public override string ToString() =>
base_perspective + '[' + String.Join(",", options) + ']';
}
As you can see, I've put together a custom class Perspective that converts to and from the JSON string, but I can't seem to get Newtonsoft JSON to automatically convert the string to my Perspective class.
I tried getting it to call the string constructor with the [JsonConstructor] attribute, but it just calls the constructor with null, not with the string value present in the JSON.
I was under the impression (based on https://stackoverflow.com/a/34186322/529618) that JSON.NET would use implicit/explicit string conversion operators to convert a simple string in JSON to an instance of the target type when available, but it seems to ignore it, and just returns the error:
Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type Perspective. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'configured_perspective'
I'm trying to avoid resorting to writing a custom JsonConverter for my Example class - I was pretty sure there would be an out-of-the-box way to convert simple string values to a non-string property type, I just haven't found it yet.

I actually wrote out a custom serializer class before doing reading the last of your article, but I then had an idea.
What if we modified example to not serialize it to Perspective? And we were somewhat lazy about it?
public class Example
{
public string id { get; set; }
public int number { get; set; }
public string configured_perspective { get; set; }
private Perspective _configuredPespective;
[JsonIgnore]
public Perspective ConfiguredPerspective => _configuredPerspective == null ? new Perspective(configured_persective) : _configuredPerspective;
}
It's not perfect, and we hold onto the string wasting memory, but it might work for you as a work-around.

Currently I'm using the following variation on #Jlalonde's suggestion - tweaked such that the user experience doesn't change, taking advantage of the fact that JSON.NET looks for private properties as well.
public class Example
{
public string id { get; set; }
public int number { get; set; }
[JsonIgnore]
public Perspective configured_perspective { get; set; }
[DataMember(Name = "configured_perspective")]
private string configured_perspective_serialized
{
get => configured_perspective?.ToString();
set => configured_perspective = value == null ? null : new Perspective(value);
}
}

Related

T object type defined by another string in same class

I'm pulling some data from an external API, and they have some objects defined by a discriminator string.
An example is an array of "Include" objects, where each one can have a different type of object as the Attributes parameter.
public class Include
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("attributes")]
public T Attributes { get; set; }
}
How can I define what object type T is, based on the Type parameter's value?
You could define Attributes as a JObject, then do .ToObject based on what the Type value is.
public class Include
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("attributes")]
public JObject Attributes { get; set; }
}
Then do an if (or switch) statement to handle each Type:
if (include.Type == "TypeOne")
ProcessTypeOne(include.Attributes.ToObject<TypeOne>());
else
...
That's assuming you have a class defined for each possible "Type". Or you can process them however you need to, just convert the Attributes to the necessary Type via the .ToObject<>() method.
You could do something like this:
using System;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
//setup
var a = new Include{Type = "Baz", Attributes = new Baz{Prop1 = "hello"}};
//convert
var b = Convert.ChangeType(a.Attributes, Type.GetType(a.Type));
//use
Console.WriteLine(b.Prop1);
}
}
public class Include
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("attributes")]
public dynamic Attributes { get; set; }
}
public class Baz
{
public string Prop1 { get; set; }
}
Convert.ChangeType would allow you to convert the dynamic object to whatever type it needed to be. However you may still find that you are in same place as #BraianM's answer and will have to type check so you know that a property or method is there.
You could also look at JsonConverter. Perhaps there is something there that would help.

How can I deserialize JSON to a custom type which has a property of type IEnumerable<dynamic>?

I'm writing a console app to retrieve JSON data from a 3rd party API. I have no control over the API data structures or functionality.
Several of the calls I make will return multiple 'pages' of data. The data is a collection of objects of a certain type e.g. User.
I have created classes in my app to match the various data types from the API.
public class User
{
[JsonProperty("id")]
public int ID { get; set; }
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
}
public class FooBar
{
[JsonProperty("foo")]
public string Foo { get; set; }
[JsonProperty("bar")]
public string Bar { get; set; }
}
The API response is always in the same format for these calls. While the actual object types in the "data" array will differ depending on what call has been made.
{
"paging":{"page":1},
"data":[{<object>}, {<object>}, {<object>},...]
}
I have created a class to try to deserialize these. The dynamic[] type for the Data property is for illustrative purposes and I am happy to change it if there is a better approach.
public class ApiResponseObject
{
[JsonProperty("paging")]
public Paging PagingInfo { get; set; }
[JsonProperty("data")]
public dynamic[] Data { get; set; }
}
And I would like to have the Data collection resolve to the appropriate type for the objects it contains. e.g.
string userJson = "{\"paging\":{\"page\":1},\"data\":[{\"id\":1,\"first_name\":\"Joe\",\"last_name\":\"Bloggs\"},{\"id\":2,\"first_name\":\"Jane\",\"last_name\":\"Doe\"}]}"; // json string would come from API
string foobarJson = "{\"paging\":{\"page\":1},\"data\":[{\"foo\":\"Lorem\",\"bar\":\"Ipsum\"},{\"foo\":\"Dolor\",\"bar\":\"Amet\"}]}";
var userResponse = JsonConvert.DeserializeObject<ApiResponseObject>(userJson);
var foobarResponse = JsonConvert.DeserializeObject<ApiResponseObject>(foobarJson);
The deserialization succeeds but the Data collection is of type JObject and cannot be cast into the correct type (User, FooBar).
I am trying to avoid having to write specific response object classes for each request if possible.
I will know what type of object I am expecting in the collection when I am requesting it so I could pass that type to the deserializer but I'm not clear on how to achieve that in this particular scenario.
Something like the below psuedo code would be ideal.
var userResponse = JsonConvert.DeserializeObject<ApiResponseObject<User>>(userJson);
Thanks for your help!
You can use the generic type T, like this :
public class ApiResponseObject<T>
{
[JsonProperty("paging")]
public Paging PagingInfo { get; set; }
[JsonProperty("data")]
public T[] Data { get; set; }
}

Deserialize JSON to C# Objects with Childs

I have a JSON string and I need some help to deserialize it.
At the moment my result is always null.
var results = JsonConvert.DeserializeObject<Root>(json);
// result == null
My JSON:
{"First":{"FirstData1":{"date":"2018-01-01","hint":""},
"FirstData2":{"date":"2018-01-06","hint":""}},
"Second":{"SecondData1":{"date":"2018-01-01","hint":""},
"SecondData2":{"date":"2018-01-06","hint":""}}}....
Only on the last Node there is actual property naming...
MyObjects
public class Root
{
public IEnumerable<TempModelRoot> Value{ get; set; }
}
public class TempModelRoot
{
[JsonProperty("Key")]
public string Key { get; set; }
[JsonProperty("Value")]
public List<TempModelChild> Value { get; set; }
}
public class TempModelChild
{
[JsonProperty("Key")]
public string Key { get; set; }
[JsonProperty("Value")]
public TempModelInfo Value { get; set; }
}
public class TempModelInfo
{
[JsonProperty("date")]
public string date { get; set; }
[JsonProperty("hint")]
public string hint { get; set; }
}
In addition to #MX D 's answer, I want to add two more useful model generator sites, which takes JSON as an input and gives appropriate model classes.
Json2Csahrp
JsonUtils
Use, whenever you find difficult to generate complex model classes.
Most likely you are having a mismatch between the model you are trying to deserialize to, and the actual expected model based of the json itself.
A easy way to resolve this is by using a tool such as Quick Types Model Generator(unafiliated) which allows you to generate C# models based upon a provided json file.
After generation you can compare and/or replace your models with the generated models.
To spot and resolve the issue with your model.

Casting derived class to base class keeps knowledge of derived when using generics

I have a weird scenario that I can't seem to wrap my head around. I have the following base class:
public class Note
{
public Guid Id { get; set; }
public string SenderId { get; set; }
...
}
Which is then derived by the following class:
public class NoteAttachment : Note
{
public string FileType { get; set; }
public string MD5 { get; set; }
...
}
I use these classes to communicate with a server, through a generic wrapper:
public class DataRequest<T> : DataRequest
{
public T Data { get; set; }
}
public class DataRequest
{
public string SomeField { get; set; }
public string AnotherField { get; set; }
}
So I have a NoteAttachment sent to the method, but I need to wrap a Note object to send to the server. So I have the following extension method:
public static DataRequest<T> GetDataRequest<T>(this T data)
{
DataRequest<T> dataRequest = new DataRequest<T>
{
SomeField = "Some Value",
AnotherField = "AnotherValue",
Data = data
};
return dataRequest;
}
Now the problem. Calling the extension method in the following way works fine, however even though the DataRequest type is DataRequest<Note>, the Data field is of type NoteAttachment.
var noteAttachment = new NoteAttachment();
...
Note note = (Note)noteAttachment;
var dataRequest = note.GetDataRequest();
Debug.WriteLine(dataRequest.GetType()); //MyProject.DataRequest`1[MyProject.Note]
Debug.WriteLine(dataRequest.Data.GetType()); //MyProject.NoteAttachment <--WHY?!
What am I doing wrong?
You are mixing two things: run-time type of an object and compile type of a field.
Type of Data field is still Note. You can verify for yourself with reflection. For example, the following will print "Note":
Console.Write(
typeof(DataRequest<Note>).GetProperty("Data").PropertyType.Name);
The Type of the object that this field contains can be Note or any derived type. Assigning an object to the variable of a base class does not change its run-time class. And since GetType() returns the type of an object you get the actual derived type (NoteAttachment).
As #Alexi answered your first question, ill try the second question in the comment. Add a KnownType attribute to your note class like this:
[KnownType(typeof(NoteAttachment)]
public class Note

ServiceStack.Text JSON parsing on .Net 4.0

H chaps,
I am trying to use ServiceStack.Text for JSON parsing (it seems to be performing better than JSON.Net in various benchmarks I have seen). But I am not getting the results I am expecting. The class I am attempting to deserialize looks like this:
[DataContract]
public class RpcRequest<T>
{
[JsonProperty("id")]
[DataMember(Name="id")]
public String Id;
[JsonProperty("method")]
[DataMember(Name="method")]
public String Method;
[JsonProperty("params")]
[DataMember(Name="params")]
public T Params;
[JsonIgnore]
[IgnoreDataMember]
public Policy Policy;
}
And I am invoking the parser like this
public static class Json
{
public static T Deserialize<T>(string serialized)
{
return TypeSerializer.DeserializeFromString<T>(serialized);
}
}
...
RpcRequest<Params> myRequeset = Json.Deserialize(packet);
However I am getting an instance back from that call which has none of the values set. ie Id, Method and Params are all null. Am I using this API correctly?
It seems that ServiceStack does not support public fields, only public properties. So if I change my model object to the following it all works.
[DataContract]
public class RpcRequest<T>
{
[JsonProperty("id")]
[DataMember(Name="id")]
public String Id { get; set; }
[JsonProperty("method")]
[DataMember(Name="method")]
public String Method { get; set; }
[JsonProperty("params")]
[DataMember(Name="params")]
public T Params { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public Policy Policy { get; set; }
}
Note the addition of getters and setters to each property.
I think you want JsonSerializer instead of TypeSerializer.
TypeSerializer is a new-fangled JSV format that Mr Mythz details on his blog here: http://www.servicestack.net/mythz_blog/?p=176

Categories

Resources