This is a continuation of a previous question of mine. The solution worked for what I was trying to do at the time but broke a whole lot of my code. I know it was bad practice not to make sure beforehand but yeah... you live and learn.
Anyhoo, here's the Q: an easy way to serialise c# objects
What I want to know is: is there any way to get the NewtonSoft Library to handle this stuff? If yes, how? If no, suggestions?
What i'm doing is chatting to a RoR3 app using json, now I cant deserialise the response. here's a little code:
The response i'm getting from RoR looks like:
[{"directory":{"created_at":"2011-07-20T22:29:38Z","drive_info":1,"id":15,"name":"New Drive","parent":0,"size":0,"updated_at":"2011-07-20T22:29:39Z","user":1}}]
I'm trying to deserialise it into a list of Directory objects using:
public static CompositeCollection deserialise<T>(string json)
{
CompositeCollection result = new CompositeCollection();
JArray arr = JArray.Parse(json);
foreach (JObject obj in arr)
{
result.Add(JsonConvert.DeserializeObject<T>(obj.First.First.ToString()));
}
return result;
}
and the relevant part of the directory class looks like:
// [Serializable]
// [DataContract]
public class Directory
{
// [DataMember]
public int id { get; set; }
// [DataMember]
public string name { get; set; }
// [DataMember]
public int parent { get; set; }
// [DataMember]
public int drive_info { get; set; }
// [DataMember]
public int size { get; set; }
// [DataMember]
public int user { get; set; }
// [DataMember]
public string state
{
get
{
/* if (parent == 0)
return _state.identify();
Directory parental;
return parental.state;*/
if (parental != null)
return parental.state;
return _state.identify();
}
set
{
_state = StateFactory.getState(value);
}
}
//[JsonIgnore]
blah...
I can deserialise most of the time by uncommenting [Serializable] (sometimes i get the following error: Object of type 'System.Int32' cannot be converted to type 'OffloadUI.Directory'. still investigating), and I can serialise by uncomenting [DataContract] and all instances of [DataMember]. What i need is something that will work in both directions.
Thanks to Zootius I found a useful guide. Here's what i did, works like a bought one:
[JsonObject(MemberSerialization.OptIn)]
public class Directory
{
[JsonProperty]
public int id { get; set; }
[JsonProperty]
public string name { get; set; }
[JsonProperty]
public int parent { get; set; }
Related
passing a Json value like this one(this will be the var jsonValue in code):
"{\"Something\":0,\"Something2\":10,\"Something3\":{\"Something4\":17,\"Something5\":38042,\"Something6\":38043,\"Id\":215},\"Something7\":215,\"SomethingId\":42,\"Something8\":\"AString, Gläser\",\"Something8\":\"44-55-18\",\"Status\":{\"Caption\":\"Fixed\",\"Value\":7},\"Type\":\"Article\",\"Id\":97,\"#Delete\":true,\"Something9\":\"8\"}"
to the following code:
var deserializer = new JsonSerializer();
const string regex = #"/Date\((.*?)\+(.*?)\)/";
var reader = new JsonTextReader(new StringReader(jsonValue));
returnValue = deserializer.Deserialize(reader, type);
type is the typeof https://dotnetfiddle.net/LMPEl0 (thank you Craig) (sorry for the weird names, can't disclose the actual ones...)
The jsonvalue is generated by input in an editable cell of a DataTable and apparently places previously null values in the end of the json string.
I get a null value in the "Something9" property in the returnValue, instead of 8(Something9 was null before and set to 8 through an editable Cell of a DataTable)
Is there some problem with the Json value that I can't see?
Or do I need some setting in the Deserializer?
Thanks
You don't show what your type is so I generated one using http://json2csharp.com.
public class Something3
{
public int Something4 { get; set; }
public int Something5 { get; set; }
public int Something6 { get; set; }
public int Id { get; set; }
}
public class Status
{
public string Caption { get; set; }
public int Value { get; set; }
}
public class RootObject
{
public int Something { get; set; }
public int Something2 { get; set; }
public Something3 Something3 { get; set; }
public int Something7 { get; set; }
public int SomethingId { get; set; }
public string Something8 { get; set; }
public Status Status { get; set; }
public string Type { get; set; }
public int Id { get; set; }
[JsonProperty("#Delete")]
public bool Delete { get; set; }
public string Something9 { get; set; }
}
Because one of your properties has a name that is not valid as a .NET property I added the [JsonProperty] attribute to that one. After that it worked perfectly. Perhaps the problem is with how you declared the #Delete JSON property in your .NET type. Given that Something9 comes after that property it would be my guess that that's part of the problem.
Here's the fiddle.
https://dotnetfiddle.net/McZF9Q
While Craig's answer helped a lot and finally led to a solution the exact answer to the problem was the following:
The Status object is an Enum and was not Deserialized correctly.
Due to that, anything that followed in the Json string was also not deserialized.
Implementing a custom Enum Deserializer was the solution. There are other Questions in stackoverflow that helped with this, particularly this one here:
How can I ignore unknown enum values during json deserialization?
Thank you everyone :)
I want to create an Windows Forms application that uses data from football-data.org. This is my first time I'm working with restful/JSON and I'm stuck.
When I try to get all of the leagues that football-data.org provides this is the response you get from the football-data.org api : response
I use the following code to get all of the data correct:(lbResultBox is a listbox)
private void btAllLeagues_Click(object sender, EventArgs e)
{
lbResultBox.Items.Clear();
List<Leagues> LL = GetLeagues("soccerseasons");
foreach (var item in LL)
{
lbResultBox.Items.Add(item.caption);
}
}
public static List<Leagues> GetLeagues(string endurl)
{
var syncClient = new WebClient();
var content = syncClient.DownloadString(baseurl + endurl);
return JsonConvert.DeserializeObject<List<Leagues>>(content);
}
public class Leagues
{
public IDictionary<string,LeagueLinks> _links { get; set; }
public string caption { get; set; }
public string league { get; set; }
public string year { get; set; }
public string numberOfTeams { get; set; }
public string numberOfGames { get; set; }
public string lastUpdated { get; set; }
}
public class LeagueLinks
{
public string href { get; set; }
}
This works.
But when I try to get all the teams from a league this is the response i get from the api. I use this code:
private void btAllTeams_Click(object sender, EventArgs e)
{
List<LeagueTeams> LT = GetLeagueTeams("soccerseasons/" + id + "/teams");
}
public static List<LeagueTeams> GetLeagueTeams(string endurl)
{
var syncClient = new WebClient();
var content = syncClient.DownloadString(baseurl + endurl);
return JsonConvert.DeserializeObject<List<LeagueTeams>>(content);
}
public class LeagueTeams
{
public IDictionary<string, LeagueLinks> _links { get; set; }
public string count { get; set; }
public IDictionary<string, LeagueTeam> teams { get; set; }
}
public class LeagueTeam
{
public IDictionary<string, TeamLinks> _links { get; set; }
public string name { get; set; }
public string code { get; set; }
public string shortName { get; set; }
public string squadMarketValue { get; set; }
public string crestUrl { get; set; }
}
public class TeamLinks
{
public string href { get; set; }
}
But i get the following error:
An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll
Additional information: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type System.Collections.Generic.IDictionary`2[System.String,FootballTestApplication.LeagueTeam]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
When i look at what the api give's me for response I can see a difference in how the response begins. With collecting all of the leagues it starts and ends with brackets ([]) but when collecting the teams in a league it doesn't start or end with brackets. When I Add the brackets myself I still get the error. This is how I add the brackets:
return JsonConvert.DeserializeObject<List<LeagueTeams>>("["+content+"]");
What am I doing wrong here?
The problem lies in the fact that the JSON returned is NOT an Array, List, Dictionary or other Enumerable but is instead an object of it's own.
If we take your JSON from that API link and go through it element by element, we learn that your Type is wrong.
You tell the JSON Serializer that the root object is a List<LeagueTeams>, but it is not. It is in fact a single LeagueTeams object.
If we make that modification to the deserializer, and we make a couple modifications to your LeagueTeams class (mostly the fact that you were deserializing the links property wrong), you are all set:
public class LeagueTeams
{
public List<IDictionary<string, string>> _links { get; set; }
public string count { get; set; }
public List<LeagueTeam> teams { get; set; }
}
return JsonConvert.DeserializeObject<LeagueTeams>(content);
I've attached an image of the results.
Additional Notes
Another thing to note: the JSON to C# website (http://json2csharp.com/) does NOT handle this JSON correctly, it in fact generates an object that is almost correct, but not quite. (Though, the generated object will still work properly, it is not an entirely correct mapping of the input JSON.) This is due, in part, to the fault of the generator, but also the website. It attempts to generate a list of a two-property Link element for the root links item, where it instead should have used a list of a dictionary to get correct mappings. (Though, this is a bit befuddling, it is the most correct approach.) Another thing to keep into consideration, yes, it does fine for getting a good start, but generally speaking, proper JSON handling requires more meticulous inspection of the generated objects to guarantee correctness.
I am hitting an API that returns some JSON as follows:
{"stats":{"X":{"Name":"X","Found":"Yes"}},"response":"OK","runtimeMs":798}
I would like to generate C# classes for it and I used json2sharp, it generated classes such as root object which i modified as follows:
public class RootObject
{
public Stats stats { get; set; }
public string response { get; set; }
public int runtimeMs { get; set; }
}
public class Stats
{
public string name { get; set; }
}
public class Variant
{
public string name { get; set; }
public string Found { get; set; }
}
The issue i am facing is that in the class Stats I have used name since the json will reply with any name such as X or Y or Z.
I am able to deserialise the JSON into the root object but cannot get any data into the stats class.
JsonConvert.DeserializeObject<RootObject>(response.Content);
Any ideas why i might be doing incorrectly?
Your problem is similar to this How can I parse a JSON string that would cause illegal C# identifiers?
So, your model should be
public class Variant
{
public string Name { get; set; }
public string Found { get; set; }
}
public class RootObject
{
public Dictionary<string, Variant> stats { get; set; }
public string response { get; set; }
public int runtimeMs { get; set; }
}
EDIT
#evanmcdonnal if use a dictionary as shown in the EZI's answer or an object with fields named X, Y, and Z you have to perform nullity or keyexists checks all over the place in order to safely use the object.
I don't think this simple linq is hard to write
rootObj.stats.Values.Where(....);
or
rootObj.stats.Keys.Select(....);
That is happening because json.NET is looking for a property name in your class which matches the field name in the json. Since name does not resemble anything in the json it finds nothing.
There are a couple of options for how to work around this. You can use an annotation to say what the json field you want to put there is, or you can change the name of the property. However, from you post it sounds like you're saying there could be an object called X or an object called Y in the json, neither of those options will make that deserialize correctly in all cases. I'll edit with some ideas for how you may want to handle that.
Based on OP's comment, here's what you're looking for;
public class RootObject
{
public Stats stats { get; set; }
public string response { get; set; }
public int runtimeMs { get; set; }
}
public class Stats
{
public Varient X { get; set; }
public Varient Y { get; set; }
public Varient Z { get; set; }
}
public class Variant
{
public string name { get; set; }
public string Found { get; set; }
}
In other places in your code you just have to have two code paths or do a conversion to avoid doing nullity checks everywhere.
if (myInstance.X != null)
// it was X that was in the json
else if (myInstace.Y != null)
// it was Y
else
// not sure what happened, perhaps neither X nor Y were present.
Now the other option is to make this just an intermediary then you define another type that just has one property named name and you give it a constructor that takes a RootObject and assigns whichever value isn't null to it's name property or whatever you want to call it. I would probably do something like this myself because if use a dictionary as shown in the EZI's answer or an object with fields named X, Y, and Z you have to perform nullity or keyexists checks all over the place in order to safely use the object.
I've been trying to parse this JSON string. I'm using JSON.NET and a snippet of the JSON, my classes and basic function calls follow:
{"status":"ok","apirate":"0","people":{
"Mike":{"id":"Mike","rating":"0.80","questions":"100"},
"Donald":{"id":"Donald","rating":"0.7","questions":"9"},
"Tony":{"id":"Tony","rating":"0.22","questions":"2"},
"Penelope":{"id":"Penelope","rating":"0.006","questions":"6"},
"Sarah":{"id":"Sarah","rating":"0.79","questions":"20"},
"Thomas":{"id":"Thomas","rating":"0.12","questions":"25"},
"Gail":{"id":"Gail","rating":"0.44","questions":"35"}}}
The classes I'm using as storage objects:
public class Folks
{
public Folks()
{
}
public String status;
public String message; //optional
public int apirate;
public PeopleDetails[] people;
}
public class PeopleDetails
{
public PeopleDetails ()
{
}
public String id;
public double rating;
public int questions;
}
And finally, what I'm doing in the code:
Folks test = new Folks();
test = JsonConvert.DeserializeObject<Folks>(myRequest.GetResponse());
Status and API rate are coming through fine, message doesn't exist because there's no error and my PeopleDetails array is making an exception. (EDIT: throwing a JsonSerializationException because the type requires a JSON array to deserialize correctly.) I've tried putting another class/object between the two I've pasted here and I've tried different collections, and so on.
So... since this is my first time working with this (smart, pick the complex stuff the first time) can anybody point me towards a solution?
Thanks in advance.
Well, first, your given JSON is incorrect, there is a { missing in the penelope record.
so the correct JSON would be
{"status":"ok","apirate":"0","people":{
"Mike":{"id":"Mike","rating":"0.80","questions":"100"},
"Donald":{"id":"Donald","rating":"0.7","questions":"9"},
"Tony":{"id":"Tony","rating":"0.22","questions":"2"},
"Penelope":{"id":"Penelope","rating":"0.006","questions":"6"},
"Sarah":{"id":"Sarah","rating":"0.79","questions":"20"},
"Thomas":{"id":"Thomas","rating":"0.12","questions":"25"},
"Gail":{"id":"Gail","rating":"0.44","questions":"35"}}}
Then, if you have a look at the structur, you may not that people is not a list but a dictionary, with the name as the key.
So, here is a working test
[TestMethod]
public void Test()
{
var json = "{\"status\":\"ok\",\"apirate\":\"0\",\"people\":{\n\"Mike\":{\"id\":\"Mike\",\"rating\":\"0.80\",\"questions\":\"100\"},\n\"Donald\":{\"id\":\"Donald\",\"rating\":\"0.7\",\"questions\":\"9\"},\n\"Tony\":{\"id\":\"Tony\",\"rating\":\"0.22\",\"questions\":\"2\"},\n\"Penelope\":{\"id\":\"Penelope\",\"rating\":\"0.006\",\"questions\":\"6\"},\n\"Sarah\":{\"id\":\"Sarah\",\"rating\":\"0.79\",\"questions\":\"20\"},\n\"Thomas\":{\"id\":\"Thomas\",\"rating\":\"0.12\",\"questions\":\"25\"},\n\"Gail\":{\"id\":\"Gail\",\"rating\":\"0.44\",\"questions\":\"35\"}}}";
var folks = JsonConvert.DeserializeObject<Folks>(json);
Assert.AreEqual("ok", folks.Status);
}
public class Folks
{
public Folks()
{
this.People = new Dictionary<string, PeopleDetails>();
}
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("apirate")]
public int Apirate { get; set; }
[JsonProperty("people")]
public Dictionary<string, PeopleDetails> People { get; set; }
}
public class PeopleDetails
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("rating")]
public decimal Rating { get; set; }
[JsonProperty("questions")]
public int Questions { get; set; }
}
I have code structured like below.
public class Stats
{
public string URL { get; set; }
public string Status { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int Length { get; set; }
}
and
public class UrlStats
{
public string URL { get; set; }
public int TotalPagesFound { get; set; }
public List<Stats> TotalPages { get; set; }
public int TotalTitleTags { get; set; }
public List<Stats> TotalTitles { get; set; }
public int NoDuplicateTitleTags { get; set; }
public List<Stats> DuplicateTitles { get; set; }
public int NoOverlengthTitleTags { get; set; }
public List<Stats> OverlengthTitles { get; set; }
}
Basically i am scanning a website for statistics like title tags, duplicate titles, etc.
I am using JQuery and making AJAX calls to webservice and retrieving url stats while the process is running to show user url stats by far collected since it takes quite a time to scan a big website. So after every 5 seconds i retrieve stats from server. Now the problem is all the List variable data i need to send at the end when scanning processing is complete, not during updates. What's happening right now the List<Stats> variable data is also sent during updates which is big chunk of data and i want to send only int type variables which are required to show process updates.
From searching on internet i couldn't find anything useful solving my problem and i found that Json.NET is very good library but i really don't know how to properly use it to get what i want.
Basically i am looking for serializing properties depending on their datatype at runtime, if its possible.
There are two different approaches for your problem.
You should choose the first one if you are going to change your classes more often because the first approach prevents that you forget to serialize a newly added property. Furthermore it is much more reusable if you want to add another classes you want to be serialized the same way.
If you have only these two classes and it's most likely that they're not going to change you can choose the second approach to keep your solution simple.
1. Use a custom converter to select all int properties
The first approach is to use a custom JsonConverter which serializes a class or struct by only including properties which have type int. The code might look like this:
class IntPropertyConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// this converter can be applied to any type
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// we currently support only writing of JSON
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
serializer.Serialize(writer, null);
return;
}
// find all properties with type 'int'
var properties = value.GetType().GetProperties().Where(p => p.PropertyType == typeof(int));
writer.WriteStartObject();
foreach (var property in properties)
{
// write property name
writer.WritePropertyName(property.Name);
// let the serializer serialize the value itself
// (so this converter will work with any other type, not just int)
serializer.Serialize(writer, property.GetValue(value, null));
}
writer.WriteEndObject();
}
}
Then you have to decorate your class with a JsonConverterAttribute:
[JsonConverter(typeof(IntPropertyConverter))]
public class UrlStats
{
// ...
}
Disclaimer: This code has been tested only very roughly.
2. Choose properties individually
The second solution looks a bit simpler: You can use the JsonIgnoreAttribute to decorate the attributes you want to exclude for serialization. Alternatively you can switch from "blacklisting" to "whitelisting" by explicitly including the attributes you want to serialize. Here is a bit of sample code:
Blacklisting: (I've reordered the properties for a better overview)
[JsonObject(MemberSerialization.OptOut)] // this is default and can be omitted
public class UrlStats
{
[JsonIgnore] public string URL { get; set; }
[JsonIgnore] public List<Stats> TotalPages { get; set; }
[JsonIgnore] public List<Stats> TotalTitles { get; set; }
[JsonIgnore] public List<Stats> DuplicateTitles { get; set; }
[JsonIgnore] public List<Stats> OverlengthTitles { get; set; }
public int TotalPagesFound { get; set; }
public int TotalTitleTags { get; set; }
public int NoDuplicateTitleTags { get; set; }
public int NoOverlengthTitleTags { get; set; }
}
Whitelisting: (also reordered)
[JsonObject(MemberSerialization.OptIn)] // this is important!
public class UrlStats
{
public string URL { get; set; }
public List<Stats> TotalPages { get; set; }
public List<Stats> TotalTitles { get; set; }
public List<Stats> DuplicateTitles { get; set; }
public List<Stats> OverlengthTitles { get; set; }
[JsonProperty] public int TotalPagesFound { get; set; }
[JsonProperty] public int TotalTitleTags { get; set; }
[JsonProperty] public int NoDuplicateTitleTags { get; set; }
[JsonProperty] public int NoOverlengthTitleTags { get; set; }
}
Oh got it, re-reading your question I think you can serialize a projection of your data.
You can try the following:
var json = JsonConvert.SerializeObject(new { u.TotalPagesFound, u.TotalTitleTags, u.NoDuplicateTitleTags, u.NoOverlengthTitleTags } );
This will convert to JSON only the int properties of your class. This is the easiest way, and it is tied to the structure of your class. If you want something more general, you will need to implement a custom converter.
Original answer:
I see no problem with your classes, you don't have anything weird like loop references, so Json.NET should have no problem serializing your class. So go grab Json.NET and then you can attempt the following
// create new instance of your url stat class
var u = new UrlStats() { URL = "a.com", TotalPages = new List<Stats>() { new Stats() { URL = "b.com", Status = "xxxx" } } };
// seralize!
var json = JsonConvert.SerializeObject(u);
Console.Write(json);
What I get with this method is something like this:
{"URL":"a.com","TotalPagesFound":0,"TotalPages":[{"URL":"b.com","Status":"xxxx","Title":null,"Description":null,"Length":0}],"TotalTitleTags":0,"TotalTitles":null,"NoDuplicateTitleTags":0,"DuplicateTitles":null,"NoOverlengthTitleTags":0,"OverlengthTitles":null}
And that looks like good json to me.