Why is JSON.NET is not working with inheritance while deserializing - c#

I am deserializing the JSON string to root object by using the following class which works fine .
[Serializable]
public class MoviesListRootObject
{
public int count { get; set; }
public Pagination pagination { get; set; }
public List<Response> response { get; set; }
}
...................................
var json = wc.DownloadString(jsonRequestURL);
var rootObj = JsonConvert.DeserializeObject<MoviesListRootObject>(json);
But if I am generalizng the root object bt creating parent class and then inheriting from it , then I get null after deserialization!!!!
[Serializable]
public class RootObject
{
public int count { get; set; }
public Pagination pagination { get; set; }
}
[Serializable]
public class MoviesListRootObject:RootObject
{
public List<MovieResponse> movieResponse { get; set; }
}
..............................................
var json = wc.DownloadString(jsonRequestURL);
var rootObj = JsonConvert.DeserializeObject<MoviesListRootObject>(json);

It is quite simple and out of the box support provided by json.net, you just have to use the following JsonSettings while serializing and Deserializing:
JsonConvert.SerializeObject(graph, Formatting.None, new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});
and for Deserializing use the below code:
JsonConvert.DeserializeObject(Encoding.UTF8.GetString(bData), type,
new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.All
});
Just take a note of the JsonSerializerSettings object initializer, that is important for you.

Assuming json string looks like the following
{"movieResponse":[{"Rating":"Good"}],"count":1,"pagination":{"PageIndex":1}}
I find it works fine with me. I am currently using Json.net 4.5 r11
If you are serialised object when the class structure looks like
[Serializable]
public class MoviesListRootObject
{
public int count { get; set; }
public Pagination pagination { get; set; }
public List<Response> response { get; set; }
}
And the json string looks something like below
{"count":1,"pagination":{"PageIndex":1},"response":[{"Rating":"Good"}]}
And now you are using the new structure to deserialise then you will get null movieResponse since the property is changed in the new structure.
To solve this problem create a new custom jsonConverter deriving from JsonConverter and create your object programatically. Please have a look at the link json-deserialization-with-jsonnet-class to get some idea. In case if you already know about this and the problem still exists then please update the question with more detail like Json.net version used, json string, complete class structure etc
HTH.

Related

UWP C# - How to deserialize JsonObject into a class using Windows.Data.Json?

I don't want to use Newtonsoft's Json.Net library. I'm avoiding any third-party dependencies if I can help it in this project.
If I have JSON that looks like this:
{
"has_more_items": false,
"items_html": "...",
"min_position": "1029839231781429248"
}
and I have a class that looks like this:
public class TwitterJson
{
bool hasMore { get; set; } // has_more_items
string rawText { get; set; } // items_html
string nextKey { get; set; } // min_position
}
and I have a JsonObject containing the above JSON:
JsonObject theJson = JsonObject.Parse(result);
How do I deserialize the JsonObject into my class? I've been trying to find a clear example of this, and everything I've found uses Json.Net.
I've been trying to find a clear example of this, and everything I've found uses Json.Net.
Because reinventing existing functionality is a waste of time especially when all the hard work has already been done for you.
If you insist on not using it then you will have to manually construct the object model based on the expected JSON.
For example, assuming publicly available properties
public class TwitterJson {
public bool hasMore { get; set; } // has_more_items
public string rawText { get; set; } // items_html
public string nextKey { get; set; } // min_position
}
Then parsing the above to the desired object model
JsonObject theJson = JsonObject.Parse(result);
var model = new TwitterJson {
hasMore = theJson.GetNamedBoolean("has_more_items"),
rawText = theJson.GetNamedString("items_html"),
nextKey = theJson.GetNamedString("min_position")
};
As mentioned by #Dimith, you need to decorate your class with [DataContract] and [DateMember], Please refer to below code which will convert your JSON into a given object.
// Deserialize a JSON string to a given object.
public static T ReadToObject<T>(string json) where T: class, new()
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
return ser.ReadObject(stream) as T;
}
}
Class:
[DataContract]
public class TwitterJson
{
[DataMember(Name = "has_more_items")]
bool hasMore { get; set; } // has_more_items
[DataMember(Name = "items_html")]
string rawText { get; set; } // items_html
[DataMember(Name = "min_position")]
string nextKey { get; set; } // min_position
}
Sample on how to use:
var result = "{\"has_more_items\": false, \"items_html\": \"...\",\"min_position\": \"1029839231781429248\"}";
var obj = ReadToObject<TwitterJson>(result);
You have to decorate your class with [DataContract] and [DataMember] attributes. Write the json into a memory stream and deserialize using DataContractJsonSerializer
Here is a more elaborated sample.
In addition to #Nkosi's answer below are some Comparisons between JSON.net and other alternatives:
JSON.Net vs DataContractJsonSerializer
JSON.Net vs Windows.Data.Json

JSON change value

I have a JSON value like this
{"$id":"649271776","$type":"outdoorgame","Overs":50,"Balls":6,"TeamName":"TestTeam"}
I wrote a C# code like this to change the value of Overs from 50 to 10
var jsonString = sSession.GameState; //this is the value {"$id":"649271776","$type":"outdoorgame","Overs":50,"Balls":6,"TeamName":"TestTeam"}
dynamic jsonObject =
Newtonsoft.Json.JsonConvert.DeserializeObject(jsonString);
jsonObject.Overs = 10;
var modifiedJsonString = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObject);
This code is changing the value of Overs from 50 to 10. The problem I am facing when I use the above code modifiedJsonString is missing these two values
"$id":"649271776","$type":"outdoorgame"
giving the output as {Overs":10,"Balls":6,"TeamName":"TestTeam"} I want $id and $type also in the modifiedJsonString.
I want modifiedJsonString like this {"$id":"649271776","$type":"outdoorgame","Overs":10,"Balls":6,"TeamName":"TestTeam"}
Can anyone tell me how to solve this problem
The problem is that $id and $type are not valid identifiers, and can't appear as members of the returned dynamic object built by the JSON serializer. As in gldraphael's answer, the solution is to create your own concrete class to hold the deserialized object; for the properties whose names start with $ you'll need to use JsonPropertyAttribute to remap the names:
public class GameState
{
[JsonProperty("$id")] public string ID { get; set; }
[JsonProperty("$type")] public string Type { get; set; }
int Overs { get; set; }
int Balls { get; set; }
public string TeamName { get; set; }
}
Further, Json.NET treats $type as a special property name and this interferes with proper deserialization of your object. To get around this, we must use the MetadataPropertyHandling.Ignore serializer setting.
Thus you can deserialize, modify and re-serialize like this:
string jsonString = "{\"$id\":\"649271776\",\"$type\":\"outdoorgame\",\"Overs\":50,\"Balls\":6,\"TeamName\":\"TestTeam\"}";
JsonSerializerSettings settings = new JsonSerializerSettings() { MetadataPropertyHandling = MetadataPropertyHandling.Ignore };
GameState jsonObject = JsonConvert.DeserializeObject<GameState>(jsonString, settings);
jsonObject.Overs = 10;
var modifiedJsonString = JsonConvert.SerializeObject(jsonObject);
See it in action.
You can use JToken to handle this.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
var jsonString = "{\"$id\":\"649271776\",\"$type\":\"outdoorgame\",\"Overs\":50,\"Balls\":6,\"TeamName\":\"TestTeam\"}";
JToken jsonObject = JToken.Parse(jsonString);
jsonObject["Overs"] = 10;
var modifiedJsonString = JsonConvert.SerializeObject(jsonObject);
// In case one wanted to update the $type and $id fields
jsonObject["$type"] = "asdf";
jsonObject["$id"] = 123456;
var modifiedJsonString2 = JsonConvert.SerializeObject(jsonObject);
Will result in:
modifiedJsonString --> {"$id":"649271776","$type":"outdoorgame","Overs":10,"Balls":6,"TeamName":"TestTeam"}
And if you needed to update $id and $type, that is possible, too.
modifiedJsonString2 -->
{"$id":123456,"$type":"asdf","Overs":10,"Balls":6,"TeamName":"TestTeam"}
Demo on .NET Fiddle: https://dotnetfiddle.net/a370Mv
Use a concrete class. You'll need to annotate the fields with $ prefixes manually. Eg:
public class Example
{
public string Field { get; set; }
[JsonProperty("$type")]
public string Type { get; set; }
}
Here's a working example.
In your case the class will look something like:
public class ObjName
{
[JsonProperty("$id")]
public string Id { get; set; }
[JsonProperty("$type")]
public string Type { get; set; }
public int Overs { get; set; }
public int Balls { get; set; }
public string TeamName { get; set; }
}
(Just be mindful of the property case).

Deserialization of Json without name fields

I need to deserialize the following Json, which according to Jsonlint.com is valid, but ive not come across this before or cannot find examples of similar Json and how to deal with it?
[1,"Bellegrove / Sherwood ","76705","486","Bexleyheath Ctr",1354565507000]
My current system with like this:
Data class:
[DataContract]
public class TFLCollection
{ [DataMember(Name = "arrivals")]
public IEnumerable<TFLB> TFLB { get; set; }
}
[DataContract]
public class TFLB
{
[DataMember]
public string routeName { get; set; }
[DataMember]
public string destination { get; set; }
[DataMember]
public string estimatedWait { get; set; }
}
Deserializer:
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(TFLCollection));
using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(result)))
{ var buses = (TFLCollection)serializer.ReadObject(stream);
foreach (var bus in buses.TFLBuses)
{
StopFeed _item = new StopFeed();
_item.Route = bus.routeName;
_item.Direction = bus.destination;
_item.Time = bus.estimatedWait;
listBox1.Items.Add(_item);
My exsiting deserializer works with a full Json stream and iterates through it, but in my new Json I need to deserialize, it only have 1 item, so I wont need to iterate through it.
So is it possible to deserialize my Json example using a similar method than I currently do?
I would say that you are attempting to overcomplicate things. What you have is a perfectly formed json array of strings. If I were you I would deserialize that to an .net array first, and then write a 'mapper' function to copy the values across:
public TFLB BusRouteMapper(string[] input)
{
return new TFLB {
Route = input[x],
Direction = input[y],
};
}
And so on. Of course this assumes that you know what order your json is going to be in, but if you are attempting this in the first place then you must do!

prevent property from being serialized in web API

I'm using an MVC 4 web API and asp.net web forms 4.0 to build a rest API. It's working great:
[HttpGet]
public HttpResponseMessage Me(string hash)
{
HttpResponseMessage httpResponseMessage;
List<Something> somethings = ...
httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK,
new { result = true, somethings = somethings });
return httpResponseMessage;
}
Now I need to prevent some properties to be serialized. I know I can use some LINQ over the list and get only the properties I need, and generally it's a good approach, but in the present scenario the something object is too complex, and I need a different set of properties in different methods, so it's easier to mark, at runtime, each property to be ignored.
Is there a way to do that?
ASP.NET Web API uses Json.Net as default formatter, so if your application just only uses JSON as data format, you can use [JsonIgnore] to ignore property for serialization:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
public List<Something> Somethings { get; set; }
}
But, this way does not support XML format. So, in case your application has to support XML format more (or only support XML), instead of using Json.Net, you should use [DataContract] which supports both JSON and XML:
[DataContract]
public class Foo
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
//Ignore by default
public List<Something> Somethings { get; set; }
}
For more understanding, you can read the official article.
According to the Web API documentation page JSON and XML Serialization in ASP.NET Web API to explicitly prevent serialization on a property you can either use [JsonIgnore] for the Json serializer or [IgnoreDataMember] for the default XML serializer.
However in testing I have noticed that [IgnoreDataMember] prevents serialization for both XML and Json requests, so I would recommend using that rather than decorating a property with multiple attributes.
Instead of letting everything get serialized by default, you can take the "opt-in" approach. In this scenario, only the properties you specify are allowed to be serialized. You do this with the DataContractAttribute and DataMemberAttribute, found in the System.Runtime.Serialization namespace.
The DataContactAttribute is applied to the class, and the DataMemberAttribute is applied to each member you want to be serialized:
[DataContract]
public class MyClass {
[DataMember]
public int Id { get; set;} // Serialized
[DataMember]
public string Name { get; set; } // Serialized
public string DontExposeMe { get; set; } // Will not be serialized
}
Dare I say this is a better approach because it forces you to make explicit decisions about what will or will not make it through serialization. It also allows your model classes to live in a project by themselves, without taking a dependency on JSON.net just because somewhere else you happen to be serializing them with JSON.net.
This worked for me: Create a custom contract resolver which has a public property called AllowList of string array type. In your action, modify that property depending on what the action needs to return.
1. create a custom contract resolver:
public class PublicDomainJsonContractResolverOptIn : DefaultContractResolver
{
public string[] AllowList { get; set; }
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
properties = properties.Where(p => AllowList.Contains(p.PropertyName)).ToList();
return properties;
}
}
2. use custom contract resolver in action
[HttpGet]
public BinaryImage Single(int key)
{
//limit properties that are sent on wire for this request specifically
var contractResolver = Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver as PublicDomainJsonContractResolverOptIn;
if (contractResolver != null)
contractResolver.AllowList = new string[] { "Id", "Bytes", "MimeType", "Width", "Height" };
BinaryImage image = new BinaryImage { Id = 1 };
//etc. etc.
return image;
}
This approach allowed me to allow/disallow for specific request instead of modifying the class definition. And if you don't need XML serialization, don't forget to turn it off in your App_Start\WebApiConfig.cs or your API will return blocked properties if the client requests xml instead of json.
//remove xml serialization
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
I will show you 2 ways to accomplish what you want:
First way: Decorate your field with JsonProperty attribute in order to skip the serialization of that field if it is null.
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public List<Something> Somethings { get; set; }
}
Second way: If you are negotiation with some complex scenarios then you could use the Web Api convention ("ShouldSerialize") in order to skip serialization of that field depending of some specific logic.
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public List<Something> Somethings { get; set; }
public bool ShouldSerializeSomethings() {
var resultOfSomeLogic = false;
return resultOfSomeLogic;
}
}
WebApi uses JSON.Net and it use reflection to serialization so when it has detected (for instance) the ShouldSerializeFieldX() method the field with name FieldX will not be serialized.
I'm late to the game, but an anonymous objects would do the trick:
[HttpGet]
public HttpResponseMessage Me(string hash)
{
HttpResponseMessage httpResponseMessage;
List<Something> somethings = ...
var returnObjects = somethings.Select(x => new {
Id = x.Id,
OtherField = x.OtherField
});
httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK,
new { result = true, somethings = returnObjects });
return httpResponseMessage;
}
Try using IgnoreDataMember property
public class Foo
{
[IgnoreDataMember]
public int Id { get; set; }
public string Name { get; set; }
}
Works fine by just adding the:
[IgnoreDataMember]
On top of the propertyp, like:
public class UserSettingsModel
{
public string UserName { get; set; }
[IgnoreDataMember]
public DateTime Created { get; set; }
}
This works with ApiController. The code:
[Route("api/Context/UserSettings")]
[HttpGet, HttpPost]
public UserSettingsModel UserSettings()
{
return _contextService.GetUserSettings();
}
Almost same as greatbear302's answer, but i create ContractResolver per request.
1) Create a custom ContractResolver
public class MyJsonContractResolver : DefaultContractResolver
{
public List<Tuple<string, string>> ExcludeProperties { get; set; }
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (ExcludeProperties?.FirstOrDefault(
s => s.Item2 == member.Name && s.Item1 == member.DeclaringType.Name) != null)
{
property.ShouldSerialize = instance => { return false; };
}
return property;
}
}
2) Use custom contract resolver in action
public async Task<IActionResult> Sites()
{
var items = await db.Sites.GetManyAsync();
return Json(items.ToList(), new JsonSerializerSettings
{
ContractResolver = new MyJsonContractResolver()
{
ExcludeProperties = new List<Tuple<string, string>>
{
Tuple.Create("Site", "Name"),
Tuple.Create("<TypeName>", "<MemberName>"),
}
}
});
}
Edit:
It didn't work as expected(isolate resolver per request). I'll use anonymous objects.
public async Task<IActionResult> Sites()
{
var items = await db.Sites.GetManyAsync();
return Json(items.Select(s => new
{
s.ID,
s.DisplayName,
s.Url,
UrlAlias = s.Url,
NestedItems = s.NestedItems.Select(ni => new
{
ni.Name,
ni.OrdeIndex,
ni.Enabled,
}),
}));
}
You might be able to use AutoMapper and use the .Ignore() mapping and then send the mapped object
CreateMap<Foo, Foo>().ForMember(x => x.Bar, opt => opt.Ignore());
For .NET Core 3.0 and above:
The default JSON serializer for ASP.NET Core is now System.Text.Json, which is new in .NET Core 3.0. Consider using System.Text.Json when possible. It's high-performance and doesn't require an additional library dependency.
https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#newtonsoftjson-jsonnet-support
Sample (Thanks cuongle)
using System.Text.Json.Serialization;
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
public List<Something> Somethings { get; set; }
}
If you already have Newtonsoft.Json intalled and chose to use it instead, by default, [JsonIgnore] won't work as expected.
For some reason [IgnoreDataMember] does not always work for me, and I sometimes get StackOverflowException (or similar). So instead (or in addition) i've started using a pattern looking something like this when POSTing in Objects to my API:
[Route("api/myroute")]
[AcceptVerbs("POST")]
public IHttpActionResult PostMyObject(JObject myObject)
{
MyObject myObjectConverted = myObject.ToObject<MyObject>();
//Do some stuff with the object
return Ok(myObjectConverted);
}
So basically i pass in an JObject and convert it after it has been recieved to aviod problems caused by the built-in serializer that sometimes cause an infinite loop while parsing the objects.
If someone know a reason that this is in any way a bad idea, please let me know.
It may be worth noting that it is the following code for an EntityFramework Class-property that causes the problem (If two classes refer to each-other):
[Serializable]
public partial class MyObject
{
[IgnoreDataMember]
public MyOtherObject MyOtherObject => MyOtherObject.GetById(MyOtherObjectId);
}
[Serializable]
public partial class MyOtherObject
{
[IgnoreDataMember]
public List<MyObject> MyObjects => MyObject.GetByMyOtherObjectId(Id);
}

How do I deserialise a JSON object to a C# class that uses derived classes?

I have the following classes which I am serialising and deserialising to and from JSON using Json.NET:
public class Message
{
public Guid MessageId { get; set; }
public List<Task> Tasks { get; set; }
}
public class Task
{
public int TaskId { get; set; }
}
public SomeTask : Task
{
public string SomeTaskThing { get; set; }
public List<string> Operations { get; set; }
}
public OtherTask : Task
{
public int OtherTaskThing { get; set; }
public List<int> MoreOtherTaskThings { get; set; }
}
A Message basically consists of a list of SomeTask and OtherTask Tasks which I pass across the wire to a message processor that also knows about these classes.
When I serialise a Message I do the following:
string json = JsonConvert.SerializeObject(
message,
Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
My derived classes are serialised as I would expect along with $type names embedded in the JSON.
However when I deserialise the JSON string back to a Message object the deserialiser doesn't rehydrate my derived class objects. I just get a list of the base class Task.
I thought $type added to the JSON string by using TypeNameHandling = TypeNameHandling.Objects might have provided a hint to the deserialiser about which classes to instantiate, but I was wrong.
What do I need to do to deserialise objects who's base class is Task and rehydrate the derived classes into the List<Task>?
I'm limited to .NET Framework 3.5 for this project.
Make sure you are using the same JsonSerializerSettings to deserialize as well. It doesn't have to be the same instance but it must have TypeNameHandling = TypeNameHandling.Objects set in order to work on deserialization.

Categories

Resources