Deserialize JSON String with multiple possible object name in one class - c#

I'm doing a server request of 3 different users from a game.
I got this JSON String as a result:
{
"d4r1o": {
"id": 1040806,
"name": "D4R1O",
"profileIconId": 596,
"revisionDate": 1399366400000,
"summonerLevel": 30
},
"snnovox": {
"id": 65728,
"name": "SN Novox",
"profileIconId": 548,
"revisionDate": 1399369344000,
"summonerLevel": 30
},
"gmbecken": {
"id": 421545,
"name": "GM Becken",
"profileIconId": 26,
"revisionDate": 1399160360000,
"summonerLevel": 30
}
}
So in order to deserialize this string I have these classes
public class RootObject
{
public SummonerDto d4r1o { get; set; }
public SummonerDto snnovox { get; set; }
public SummonerDto gmbecken { get; set; }
}
public class SummonerDto
{
public int id { get; set; }
public string name { get; set; }
public int profileIconId { get; set; }
public long revisionDate { get; set; }
public int summonerLevel { get; set; }
}
This works just fine, but what if the user wants to make a request about his own username, for example: snyucax
{"snyucax": {
"id": 48985,
"name": "SN YucaX",
"profileIconId": 504,
"revisionDate": 1399257043000,
"summonerLevel": 30
}}
Is there a way to make only one object in the RootObject Class that let me take any username without having to especify that name as an object?

It looks like your data is better modeled as a dictionary. Use
JsonConvert.DeserializeObject<Dictionary<string, SummonerDto>>(myJson)

You should use a dictionary for this. If you're using JSON.NET, then the code would look like:
var users = JsonConvert.DeserializeObject<Dictionary<string, SummonerDto>>(jsonDataString);
Then you could get the user by going:
var user = users["d4r1o"];
e: Seems Tim S just beat me to the punch, haha.

I think you should not hardwire your objects. Instead use a dictionary or send the username and form a json object based on the user and deserialize it at the client's browser o app

Related

deserialize then serialize json with dynamic field name

I have a JSON file that looks something like this.
{
"id": "top1",
"components": [
{
"type": "resistor",
"id": "res1",
"resistance": {
"default": 100,
"min": 10,
"max": 1000
},
"netlist": {
"t1": "vdd",
"t2": "n1"
}
},
{
"type": "nmos",
"id": "m1",
"m(l)": {
"default": 1.5,
"min": 1,
"max": 2
},
"netlist": {
"drain": "n1",
"gate": "vin",
"source": "vss"
}
}
]
}
I want to make an API using oop to work with that JSON file,
I made the following classes.
public class Topology
{
[Required]
public string id { get; set; }
[Required]
public List<TopologyComponents> components { get; set; }
}
public class TopologyComponents
{
[Required]
public string type { get; set; }
[Required]
public string id { get; set; }
[Required]
public Values ???????? {get; set; }
[Required]
public Dictionary<string, string> netlist { get; set; }
}
public class Values
{
[Required]
public double #default { get; set; }
[Required]
public double min { get; set; }
[Required]
public double max { get; set; }
}
my question is those question marks????????
the field name is dynamic resistance, m(l), .....
how can I handle those cases?
I tried Jackson annotations, expandobject, and dictionaries. but none of them worked as I want.
From the looks of it, your Topology class will need to have Dictionary<string, dynamic> data type since the keys of components will be arbitrary. Even though type and id will be the same across all components, netlist and one other property will be dynamic.
Change your list of components to Dictionary<string, dynamic> and then get the data you need by first checking which property actually exists in the component.
public class Topology
{
[Required]
public string id { get; set; }
[Required]
public List<Dictionary<string, dynamic>> components { get; set; }
}
this will give you a list of dictionary with string as the keys and dynamic objects as the values. You can iterate through the keys using foreach loop on components.Keys and perhaps a switch statement to see if the key you expect exists when iterating through each component.
Example on how you can create your own list of components... not sure how you are going to use the data since that will drive the way you deserialize this,
var obj = JsonConvert.DeserializeObject<Topology>(jsonText);
List<dynamic> allComps = new List<dynamic>();
foreach(var component in obj.components)
{
var comp = new ExpandoObject() as IDictionary<string, object>;
foreach(var key in component.Keys)
{
switch (key)
{
case "id":
case "type":
comp.Add(key, component[key].ToString());
break;
case "netlist":
comp.Add(key, component[key].ToObject<Dictionary<string, string>>());
break;
default:
comp.Add(key, component[key].ToObject<Values>());
break;
}
}
allComps.Add(comp);
}

Newtonsoft json - deserialize classes with same name, but different properties

I am consuming a REST API - which returns JSON. I deserialize the JSON using NewtonSOFT JSON in C#.
The returned JSON contanins an "Answer" object that contain another "Answer" object - problem is that the 2 "Answer" objects has different properties / definitions.
How can that be handled in C# or in NewtonSoft?
Json structure
"answers": [
{
"tag": {
"id": 803,
"name": "Oplysninger om bestilling af tilstandsrapporten"
},
"option": false,
"answers": [
{
"label": "Vælg",
"value": "Ved hjælp af familie, venner eller bekendte mv",
"show_inline": false
}
],
"question": "Hvordan fandt du den bygningssagkyndige?",
"seller_question_id": 1
}
"answers" here is a property name, not a class name, so all you need to do is define your classes in a way that matches the pattern you've shown. Having two different properties named "answers" doesn't present any particular complication.
class Response
{
public IList<OuterAnswer> answers { get; set; }
}
class OuterAnswer
{
public Tag tag { get; set; }
public bool option { get; set; }
public IList<InnerAnswer> answers { get; set; }
public string question { get; set; }
public long seller_question_id { get; set; }
}
class InnerAnswer
{
public string label { get; set; }
public string value { get; set; }
public bool show_inline { get; set; }
}

Saving deserialized JSON objects to database with duplicate child entities

I am retrieving some JSON from an API call and deserializing it into it's component objects. Everything works absolutely fine, until I come to saving to the database. The reason is, there are child objects with duplicate keys (which is absolutely correct as far as the data is concerned) but when I save the top level object, it throws a primary key violation error on the child object.
Here is a sample of my JSON (I know it is not complete);
{
"count": 149,
"filters": {},
"competitions": [
{
"id": 2006,
"area": {
"id": 2001,
"name": "Africa",
"countryCode": "AFR",
"ensignUrl": null
},
"name": "WC Qualification",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 555,
"startDate": "2019-09-04",
"endDate": "2021-11-16",
"currentMatchday": null,
"winner": null
},
"numberOfAvailableSeasons": 2,
"lastUpdated": "2018-06-04T23:54:04Z"
},
{
"id": 2025,
"area": {
"id": 2011,
"name": "Argentina",
"countryCode": "ARG",
"ensignUrl": null
},
"name": "Supercopa Argentina",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 430,
"startDate": "2019-04-04",
"endDate": "2019-04-04",
"currentMatchday": null,
"winner": null
},
"numberOfAvailableSeasons": 2,
"lastUpdated": "2019-05-03T05:08:18Z"
},
{
"id": 2023,
"area": {
"id": 2011,
"name": "Argentina",
"countryCode": "ARG",
"ensignUrl": null
},
"name": "Primera B Nacional",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 547,
"startDate": "2019-08-16",
"endDate": "2020-06-14",
"currentMatchday": 30,
"winner": null
},
"numberOfAvailableSeasons": 3,
"lastUpdated": "2020-05-15T00:00:02Z"
},
Currently I am just saving the top level object, and I expect/want all of the child objects to save too. If I turn off the primary keys on the child objects (make them identidy columns rather than their actual values), this all works fine and all of the child objects save perfectly. As you can see from the JSON, "area" 2011 is a duplicate, there are two competitions that have the same area, so data wise it is correct, but with the proper primary keys of the "area" turned on, it trips out as it is trying to insert a duplicate record.
So I understand perfectly what is going on and why it is erroring, what I want to know is, is there a simple way of just telling EF to ignore duplicate key errors. I can't add a try catch to saving the top level object as it just saves nothing when it hits the error.
I have tried saving the individual child objects, testing for their existence prior to the save, but when it tries to save the parent level object, it tries to save the child object too, leaving me with the same problem.
Here is my code for saving the top level object (cut down for simplicity);
public class Area
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string name { get; set; }
public string countryCode { get; set; }
public string ensignUrl { get; set; }
}
public class Winner
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string name { get; set; }
public string shortName { get; set; }
public string tla { get; set; }
public string crestUrl { get; set; }
}
public class CurrentSeason
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string startDate { get; set; }
public string endDate { get; set; }
public int? currentMatchday { get; set; }
public Winner winner { get; set; }
}
public class Competition
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public Area area { get; set; }
public string name { get; set; }
public string code { get; set; }
public string emblemUrl { get; set; }
public string plan { get; set; }
public CurrentSeason currentSeason { get; set; }
public int numberOfAvailableSeasons { get; set; }
public DateTime lastUpdated { get; set; }
}
public class Example
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int count { get; set; }
public IList<Competition> competitions { get; set; }
}
static void Main(string[] args)
{
string json = GET(#"http://my.url.com/api/stuff");
Example example = JsonConvert.DeserializeObject<Example>(json);
using(var db = new ExampleContext())
{
db.Examples.Add(example);
db.SaveChanges();
}
}
Thanks in anticipation.
Unfortunately there isn't any straight way to overcome your problem.
EF Change Tracker tracks entities by their reference and the only way to solve your problem is to create same object for all identical areas.
For this you have two choices:
1- Loop over example after this line
Example example = JsonConvert.DeserializeObject<Example>(json);
and find all identical areas and replace all with one of them.
2- Use PreserveReferencesHandling feature of NewtonSoft. But it needs to apply on both Serialize and Deserialize sides:
Server(Api) side:
string json = JsonConvert.SerializeObject(data, Formatting.Indented,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
Client side:
var example = JsonConvert.DeserializeObject<Example>(json,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });

Json Deserialize Array

I am new to Json and trying to do some examples with it. I have Json data like this:
{
"Title": "The Avengers",
"Year": "2012",
"Rated": "PG-13",
"Released": "04 May 2012",
"Runtime": "143 min",
"Genre": "Action, Adventure, Sci-Fi",
"Director": "Joss Whedon",
"Writer": "Joss Whedon (screenplay), Zak Penn (story), Joss Whedon (story)",
"Actors": "Robert Downey Jr., Chris Evans, Mark Ruffalo, Chris Hemsworth",
"Plot": "Earth's mightiest heroes must come together and learn to fight as a team if they are going to stop the mischievous Loki and his alien army from enslaving humanity.",
"Language": "English, Russian, Hindi",
"Country": "USA",
"Awards": "Nominated for 1 Oscar. Another 38 wins & 79 nominations.",
"Poster": "https://m.media-amazon.com/images/M/MV5BNDYxNjQyMjAtNTdiOS00NGYwLWFmNTAtNThmYjU5ZGI2YTI1XkEyXkFqcGdeQXVyMTMxODk2OTU#._V1_SX300.jpg",
"Ratings": [
{
"Source": "Internet Movie Database",
"Value": "8.0/10"
},
{
"Source": "Rotten Tomatoes",
"Value": "92%"
},
{
"Source": "Metacritic",
"Value": "69/100"
}
],
"Metascore": "69",
"imdbRating": "8.0",
"imdbVotes": "1,200,683",
"imdbID": "tt0848228",
"Type": "movie",
"DVD": "25 Sep 2012",
"BoxOffice": "$623,279,547",
"Production": "Walt Disney Pictures",
"Website": "http://marvel.com/avengers_movie",
"Response": "True"
}
I can get the data and read it just fine but when it comes deserialize I get the following error:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Deneme.Modeller.Main]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
This is my code
string url = "http://www.omdbapi.com/?apikey=7663ce8e&t=Avengers";
WebRequest request = WebRequest.Create(url);
WebResponse reply;
reply = request.GetResponse();
StreamReader returninfo = new StreamReader(reply.GetResponseStream());
string getinfo = returninfo.ReadToEnd();
List<Main> Info = JsonConvert.DeserializeObject<List<Main>>(getinfo);
and for models this is first main:
public string Title { get; set; }
public string Year { get; set; }
public string Rated { get; set; }
public string Released { get; set; }
public string Runtime { get; set; }
public string Genre { get; set; }
public string Director { get; set; }
public string Writer { get; set; }
public string Actors { get; set; }
public string Plot { get; set; }
public string Language { get; set; }
public string Country { get; set; }
public string Awards { get; set; }
public string Poster { get; set; }
public List<Rating> Ratings { get; set; }
public string Metascore { get; set; }
public string imdbRating { get; set; }
public string imdbVotes { get; set; }
public string imdbID { get; set; }
public string Type { get; set; }
public string DVD { get; set; }
public string BoxOffice { get; set; }
public string Production { get; set; }
public string Website { get; set; }
public string Response { get; set; }
second one is for Ratings:
public string Source { get; set; }
public string Value { get; set; }
public virtual ICollection<Main> Mains { get; set; }
It's about Json array, but I looked asked questions about this problem and tried to fix it but no luck. What am I missing?
You are trying to deserialize a single object of type Main into a list of objects.
You can either change your code to deserialize into a single object instead of a list or you can alter your JSON to represent an array of objects.
The first option would be
Main Info = JsonConvert.DeserializeObject<Main>(getinfo);
And the second option
[{"Title":"The Avengers","Year":"2012","Rated":"PG-13","Released":"04 May 2012","Runtime":"143 min","Genre":"Action, Adventure, Sci-Fi","Director":"Joss Whedon","Writer":"Joss Whedon (screenplay), Zak Penn (story), Joss Whedon (story)","Actors":"Robert Downey Jr., Chris Evans, Mark Ruffalo, Chris Hemsworth","Plot":"Earth's mightiest heroes must come together and learn to fight as a team if they are going to stop the mischievous Loki and his alien army from enslaving humanity.","Language":"English, Russian, Hindi","Country":"USA","Awards":"Nominated for 1 Oscar. Another 38 wins & 79 nominations.","Poster":"https://m.media-amazon.com/images/M/MV5BNDYxNjQyMjAtNTdiOS00NGYwLWFmNTAtNThmYjU5ZGI2YTI1XkEyXkFqcGdeQXVyMTMxODk2OTU#._V1_SX300.jpg","Ratings":[{"Source":"Internet Movie Database","Value":"8.0/10"},{"Source":"Rotten Tomatoes","Value":"92%"},{"Source":"Metacritic","Value":"69/100"}],"Metascore":"69","imdbRating":"8.0","imdbVotes":"1,200,683","imdbID":"tt0848228","Type":"movie","DVD":"25 Sep 2012","BoxOffice":"$623,279,547","Production":"Walt Disney Pictures","Website":"http://marvel.com/avengers_movie","Response":"True"}]
(simply add brackets)
Which option you have to choose is depending on your requirements, i.e. if you want to allow multiple objects or just one.
Main Info = JsonConvert.DeserializeObject<Main>(getinfo);
Your json string has only one Main object, you were trying to get a List
You try to deserialize one JSON object into a list of objects.
This is an example of simple object:
{ "field": 123 }
To deserialize it you need to:
var obj = JsonConvert.DeserializeObject<SomeModel>(json);
But if you have an array of objects:
[{ "field": 123 }, { "field": 123 }]
You will be able to deserialize them to a list like this:
var objs = JsonConvert.DeserializeObject<SomeModel[]>(json);
or
var objs = JsonConvert.DeserializeObject<List<SomeModel>>(json);
Solutions to your question:
Change deserialization type to a single object.
Wrap your JSON around with []
when we call api 'http://www.omdbapi.com/?apikey=7663ce8e&t=Avenger' we gets an objet not an array of object
try
var info = JsonConvert.DeserializeObject<Main>(getinfo);
If you want list of movies try a other api b.e.: themoviedbAPI

Complex json deserialization

I have to deserialize the following json response (the Result list has variable length):
{
"ResultSet": {
"Query": "volkswagen",
"Result": [
{
"symbol": "VLKAY",
"name": "Volkswagen AG",
"exch": "PNK",
"type": "S",
"exchDisp": "OTC Markets",
"typeDisp": "Equity"
},
{
"symbol": "VOW3.DE",
"name": "Volkswagen AG",
"exch": "GER",
"type": "S",
"exchDisp": "XETRA",
"typeDisp": "Equity"
},
{
"symbol": "VOW.DE",
"name": "Volkswagen AG",
"exch": "GER",
"type": "S",
"exchDisp": "XETRA",
"typeDisp": "Equity"
}
]
}
}
What I got:
JavaScriptSerializer js = new JavaScriptSerializer();
string jsonString = "...String is here...";
SearchObj obj = js.Deserialize<SearchObj>(jsonString);
I understand that I usually have to create a fitting obj. e.g. SearchObj which will get filled but in this case I'm not entirely sure how this object is supposed to look like. I came up with:
class Data
{
public string symbol { get; set; }
public string name { get; set; }
public string exch { get; set; }
public string type { get; set; }
public string exchDisp { get; set; }
public string typeDisp { get; set; }
}
class Container
{
public string Query { get; set; }
public List<Data> Result { get; set; }
}
class SearchObj
{
public Container ResultSet { get; set; }
}
But guess what, it's not working, I only get ResultSet = null.
Try to change your class Container as
class Container
{
public string Query { get; set; }
public Data[] Result { get; set; }
}
I have not tested it, based on my observation
I always feel bad when I answer my own question but here it goes.
Basically my idea was correct, I only made one mistake which is that I don't need the
class SearchObj
{
public Container ResultSet { get; set; }
}
Using
Container obj = js.Deserialize<Container>(jsonString);
instead of
SearchObj obj = js.Deserialize<SearchObj>(jsonString);
made the trick. Both Data[] and List<Data> in Container work btw.
Edit:
From giammins comment it seems that it is working on some machines without that change but I guess that's a case for undefined behavior.
You can use http://www.json2charp.com to create your classes.

Categories

Resources