Deserialize JSON array which has mixed values System.Text.JSON - c#

I'm trying to create a page rendered in .net core 3.1 which renders pages based on JSON.
How can I deserialzie the JSON at the end of this post?
I've tried to deserialize this however it doesn't work because I loose the data for each Component,
since the Page class has a List<Component> - but I need this to be a list of varying different components.
Page Model :
public class Page
{
public int id { get; set; }
public string pagename { get; set; }
public string metatitle { get; set; }
public string metadescription { get; set; }
public string created_at { get; set; }
public string updated_at { get; set; }
public List<Component> components { get; set; }
}
public class Pages
{
public List<Page> pages { get; set; }
}
Component Model:
public class Component
{
public string component { get; set; }
public int id { get; set; }
}
A Component :
public class Title : Component
{
public string component { get; set; }
public int id { get; set; {
public string titletext { get; set; }
}
This is the JSON:
{
"id":1,
"pagename":"home",
"metatitle":"no title",
"metadescription":"no meta",
"created_at":"2020-05-31T16:35:52.084Z",
"updated_at":"2020-05-31T16:35:52.084Z",
"components":[
{
"component":"components.titletext",
"id":1,
"titletext":"hello"
},
{
"component":"components.section",
"id":2,
"titletext":"hello",
"descriptiontext":"its a beatiful day in lost santos",
"buttonlink":"/go"
},
{
"component":"components.cta",
"id":3,
"sectiontitle":"hello",
"buttonlink":"/go",
"buttontext":"click me"
}
]
}

If you don't want to add all properties to the Component class like that:
public class Component
{
public string component { get; set; }
public int id { get; set; }
public string titletext { get; set; }
public string sectiontitle { get; set; }
public string buttonlink { get; set; }
public string descriptiontext { get; set; }
}
You will need to write custom JsonConverter for example (not very performant implementation but works with your json and you will not need to parse every field by hand):
public class ComponentConverter : JsonConverter<Component>
{
public override Component Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using (var doc = JsonDocument.ParseValue(ref reader))
{
var type = doc.RootElement.GetProperty(#"component").GetString();
switch(type)
{
case "components.titletext":
return JsonSerializer.Deserialize<Title>(doc.RootElement.GetRawText());
// other types handling
default: return JsonSerializer.Deserialize<Component>(doc.RootElement.GetRawText());
}
}
}
public override void Write(Utf8JsonWriter writer, Component value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
public class Component
{
public string component { get; set; }
public int id { get; set; }
}
public class Title : Component
{
public string titletext { get; set; }
}
And usage example:
var json = #"[
{
""component"":""components.titletext"",
""id"":1,
""titletext"":""hello""
},
{
""component"":""components.section"",
""id"":2,
""titletext"":""hello"",
""descriptiontext"":""its a beatiful day in lost santos"",
""buttonlink"":""/go""
},
{
""component"":""components.cta"",
""id"":3,
""sectiontitle"":""hello"",
""buttonlink"":""/go"",
""buttontext"":""click me""
}
]";
var deserializeOptions = new JsonSerializerOptions();
deserializeOptions.Converters.Add(new ComponentConverter());
JsonSerializer.Deserialize<List<Component>>(json, deserializeOptions).Dump();
Also do not use this converter as parameter for JsonConverterAttribute cause it will end in stackoverflow.

If you want/need completely separate unrelated classes you could use a technique that doesn't use a converter:
var ays = new List<A>();
var bees = new List<B>();
using var doc = JsonDocument.Parse(json);
foreach (var block in doc.RootElement.EnumerateArray())
{
switch (block.GetProperty("component").GetString())
{
case "typeA": ays.Add(Deserialise<A>(block)); break;
case "typeB": bees.Add(Deserialise<B>(block)); break;
// ... case
//default: ...
}
}
var composite = new
{
As = ays,
Bs = bees
};
// This is OK, but if you need to speed it up, please have a look at
// https://stackoverflow.com/questions/58138793/system-text-json-jsonelement-toobject-workaround
static T Deserialise<T>(JsonElement e) => JsonSerializer.Deserialize<T>(e.GetRawText(), options: new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

Related

How to generate a JSON class with dynamic name

I don't know if there is an existing name for that case, but I'm trying to retrieve data from NASA API (https://api.nasa.gov/) and I have a simple challenge to catch a list of objects near earth. Here is the JSON response I have from the GET request I do to "https://api.nasa.gov/neo/rest/v1/feed?...."
{
"links": {
"next": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-04&end_date=2021-07-04&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym",
"prev": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-02&end_date=2021-07-02&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym",
"self": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-03&end_date=2021-07-03&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym"
},
"element_count": 6,
"near_earth_objects": {
"2021-07-03": [
{
"links": {
"self": "http://www.neowsapp.com/rest/v1/neo/3701710?api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym"
},
"id": "3701710",
"neo_reference_id": "3701710",
"name": "(2014 WF497)",
"nasa_jpl_url": "http://ssd.jpl.nasa.gov/sbdb.cgi?sstr=3701710",
"absolute_magnitude_h": 20.23,
"estimated_diameter": {
"kilometers": {
}
And that's the way it is built in Visual Studio (using the Special Paste option for JSON)
public class NearEarthObject
{
public Links links { get; set; }
public int element_count { get; set; }
public Near_Earth_Objects near_earth_objects { get; set; }
}
public class Links
{
public string next { get; set; }
public string prev { get; set; }
public string self { get; set; }
}
public class Near_Earth_Objects
{
public _20210703[] _20210703 { get; set; }
}
public class _20210703
{
public Links1 links { get; set; }
public string id { get; set; }
public string neo_reference_id { get; set; }
public string name { get; set; }
public string nasa_jpl_url { get; set; }
public float absolute_magnitude_h { get; set; }
public Estimated_Diameter estimated_diameter { get; set; }
public bool is_potentially_hazardous_asteroid { get; set; }
public Close_Approach_Data[] close_approach_data { get; set; }
public bool is_sentry_object { get; set; }
}
The question is, inside of the element "near_earth_objects", there is an element called "2021-07-03" (the date of the data I requested), the problem is that I am trying to include it into a DataGridView made in .NET C# (Windows Forms, but that doesn't matters here, I think) and the user wants to get the information by date. So, "2021-07-03" is a valid member just for one day, and the user should be able to get data from multiple days.
So, is there a way in C# to get all child objects inside of near_earth_objects without knowing their names since there will be the option to search for asteroids from date X to Y in my application?
Using System.Text.Json
The API response will map to the following classes
public class Neo
{
public Links Links { get; set; }
public int ElementCount { get; set; }
public Dictionary<string, List<NearEarthObject>> NearEarthObjects { get; set; }
}
public class Links
{
public string Next { get; set; }
public string Prev { get; set; }
public string Self { get; set; }
}
public class NearEarthObject
{
public Links Links { get; set; }
public string Id { get; set; }
public string Name { get; set; }
// Other properties
}
The NearEarthObjects is simply a Dictionary, where the key is the formatted date and value is a List containing NearEarthObject
The PropertyNamingPolicy will allow us to support the API's underscore property naming convention.
public class UnderscoreNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.Underscore();
}
}
Example usage
// using using System.Text.Json;
var response = await new HttpClient().GetStringAsync(url);
var neo = JsonSerializer.Deserialize<Neo>(response, new JsonSerializerOptions
{
PropertyNamingPolicy = new UnderscoreNamingPolicy()
});
foreach(var neos in neo.NearEarthObjects)
{
Console.WriteLine(neos.Key);
}
use System.Text.Json, JsonNamingPolicy
demo code
public class DynamicNamePolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
var today = DateTime.Today.ToString("yyyy-MM-dd");
if (name.Equals("DateData")) //model property name
return today; //convert to json string property name
return name;
}
}
//data deserialize
string data = ""; //json string
var obj = JsonSerializer.Deserialize<NearEarthObject>(data, new JsonSerializerOptions
{
PropertyNamingPolicy = new DynamicNamePolicy(),
});

How to deserialize where keys are numbers [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
Any thoughts on how to create the C# class for how to deserialise this correctly?
{
"key": "MobileNetworkOperator",
"name": "MNO Option",
"type": "n",
"options": [
{
"0": "Spark LTE M1"
},
{
"1": "Default"
},
{
"2": "AT&T"
},
{
"3": "Verizon"
},
{
"4": "Telstra"
},
{
"5": "T-Mobile USA"
},
{
"6": "China Telecom"
},
{
"7": "Sprint"
},
{
"8": "Vodafone"
},
{
"9": "Telus"
},
{
"10": "Deutsche Telecom"
},
{
"11": "Standard Europe"
}
],
"rebootRequired": true,
"default": 0
},
json2csharp.com gives the classes below but it does not seem to work. After deserialise, the right number of options items are present but all fields in each Option are set to null. I presume because the members are called '_0' instead of the actual '0' in the JSON???
public class Option
{
public string _0 { get; set; }
public string _1 { get; set; }
public string _2 { get; set; }
public string _3 { get; set; }
public string _4 { get; set; }
public string _5 { get; set; }
public string _6 { get; set; }
public string _7 { get; set; }
public string _8 { get; set; }
public string _9 { get; set; }
public string _10 { get; set; }
public string _11 { get; set; }
}
public class Root
{
public string key { get; set; }
public string name { get; set; }
public string type { get; set; }
public List<Option> options { get; set; }
public bool rebootRequired { get; set; }
public int #default { get; set; }
}
Note that this is part of a larger object with many of these items in which there can be no or many options in each item, but I tried to simplify it down.
I am using the System.Text.Json parser. Thanks in advance for your advice. This is my first go at JSON in C#!
OP stated that they are using System.Text.Json as their parser. So, with that being known, we will need to create a custom JsonConverter.
Start by creating an Option class:
public class Option
{
public int Key { get; set; }
public string Value { get; set; }
}
Next, create our JsonConverter implementation based on the Option object from above:
public class MyOptionConverter : JsonConverter<Option>
{
public override Option Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
reader.Read(); // read in the first token
var key = int.Parse(reader.GetString()); // get the value as an integer
reader.Read(); // read in the next token
var value = reader.GetString(); // get the value as string
reader.Read(); // read in End-of-object
return new Option { Key = key, Value = value }; // return our new object
}
public override void Write(
Utf8JsonWriter writer, Option value, JsonSerializerOptions options)
{
throw new NotImplementedException(); // we aren't going to handle writing for now.
}
}
Let's create a model that represents your data:
public class Root
{
[JsonPropertyName("key")]
public string Key { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("options")]
public IReadOnlyList<Option> Options { get; set; }
[JsonPropertyName("rebootRequired")]
public bool RebootRequired { get; set; }
[JsonPropertyName("default")]
public int Default { get; set; }
}
Then to deserialize, you would simply do this:
var options = new JsonSerializerOptions();
options.Converters.Add(new MyOptionConverter());
var myObject = JsonSerializer.Deserialize<Root>(theJson, options);
Edited To Add:
#Llama brought it to my attention (in the comments) that OP is probably using Newtonsoft and not System.Text.Json.
Someone already posted the Newtonsoft solution, but the author deleted it even though it was probably the correct answer.
I am going to leave this answer up just in case it may help someone else.
Deserializing a JSon I will always go for a class representing exactly the json structure/
Class definition using System.Text.Json:
public partial class JsonDto
{
public string key { get; set; }
public string name { get; set; }
public string type { get; set; }
public List<Dictionary<string, string>> options { get; set; }
public bool rebootRequired { get; set; }
public long #default { get; set; }
}
Class definition using Newtonsoft.Json, is the same. Json property in order to have capitilized properties.
public partial class Root
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("options")]
public List<Dictionary<string, string>> Options { get; set; }
[JsonProperty("rebootRequired")]
public bool RebootRequired { get; set; }
[JsonProperty("default")]
public long Default { get; set; }
}
The deserialization is almost the same
var result = JsonConvert.DeserializeObject<JsonDto>(input); // Newtonsoft.Json
var result = JsonSerializer.Deserialize<JsonDto>(input);
Then a simple transpormation where we ger rid of the extra list in List<Dictionary<string, string>>.
var result =
new UsableObject {
Key=jsonDto.key,
Name=jsonDto.name,
Type=jsonDto.type,
RebootRequired=jsonDto.rebootRequired,
Default=jsonDto.#default,
Options=jsonDto.options
.SelectMany(trd=> trd)
.ToDictionary(kvp=> kvp.Key, kvp=> kvp.Value),
};
Live demo : https://dotnetfiddle.net/c9rNL0

How to deserialize multidimensional JSON

I know people asked and already got some answers very similar question before like this, but still, I couldn't figure it out about mine. I have a JSON file contains a multidimensional object, like below:
{
"Common": {
"Required": "Required Entry ",
"Photos": "Photos",
"Videos": "Videos",
"Register": "Register"
},
"Forms": {
"Form": "Forms",
"Name": "Name",
"Phone": "Phone",
"Email": "Email",
"Message": "Message"
},
"Sections": {
"Home": {
"EventDateTime": "",
"MainTitle": "",
"SubTitle": ""
},
"About": {},
"Venue": {},
"Schedule": {},
"Speakers": {},
"Sponsors": {},
"Price": {},
"Contact": {}
}
}
I would like to deserialize it into my view model (LanguagesViewModel) like this:
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class LanguagesViewModel
{
public Common Common { get; set; }
public Buttons Buttons { get; set; }
public Forms Forms { get; set; }
public Navbar Navbar { get; set; }
public Sections Sections { get; set; }
}
public class Common
{
public string Required { get; set; }
public string Photos { get; set; }
public string Videos { get; set; }
public string Register { get; set; }
}
public class Forms
{
public string Form { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Message { get; set; }
}
public class Sections
{
public Home Home { get; set; }
public About About { get; set; }
public Venue Venue { get; set; }
public Schedule Schedule { get; set; }
public Speakers Speakers { get; set; }
public Sponsors Sponsors { get; set; }
public Price Price { get; set; }
public Contact Contact { get; set; }
}
public class Home
{
public string EventDateTime { get; set; }
public string MainTitle { get; set; }
public string SubTitle { get; set; }
}
public class About
{
}
public class Venue
{
}
public class Schedule
{
}
public class Speakers
{
}
public class Sponsors
{
}
public class Price
{
}
public class Contact
{
}
}
Some of the snippet to do this:
using (StreamReader sr = new StreamReader(language_file_path))
{
string contents = sr.ReadToEnd();
items = JsonConvert.DeserializeObject<LanguagesViewModel>(contents);
}
Somehow, I only can get the first level of the objects, which is:
LanguagesViewModel{
Common:null,
Forms:null,
Sections:null
}
Not the second level, not the third level. Did I do something wrong or have I missed something? Very appreciated for any kind of help.
Thank you.
You can Use this static class
public static class JsonHelper
{
public static T ToObject<T>(this string content)
{
var obj = JObject.Parse(content).GetValue(typeof(T).Name);
if (obj == null)
throw new NullReferenceException();
else
return obj.ToObject<T>();
//This ToObject here is default method written in object
}
}
Usage
var mymodel= json.ToObject<Forms>();
Or create a JSON object and read it with magic strings.
//Creating your JSON object
JObject content = JObject.Parse(sr.ReadToEnd()//or your json);
//content["your object name"] let you access to you object
var common =(Common)content["Common"];
in multidimensional objects, you can access them like this.
//content["level1"]["level2"]["level3"] & ...
var sections= (Home)content["Sections"]["Home"];
Also this way may work but i prefer the way with magic strings.
dynamic jsonObject = new JObject.Parse(sr.ReadToEnd());
var common = jsonObject.Common;
You can find more in this link
I hope this Helps!

Cant store this JSON data in c#?

Im tryig to create an application that uses the API from a site called backpack.tf.
Im facing a problem, I have a file for where the data will be stored:
class currencyData
{
public CurrencyResponse currencyResponse { get; set; }
}
public class CurrencyResponse
{
public int success { get; set; }
public int current_time { get; set; }
public int raw_usd_value { get; set; }
public int usd_currency { get; set; }
public int usd_currency_index { get; set; }
public Dictionary<string, CurrencyPrices> items { get; set; }
}
public class CurrencyPrices
{
public int currency { get; set; }
public int value { get; set; }
public int value_high { get; set; }
public int value_raw { get; set; }
public int value_high_raw { get; set; }
public int last_update { get; set; }
public int difference { get; set; }
}
and basically my code for trying to store the JSON data is this:
//make call to the API and retrieve JSON data
char[] array1 = { };
char[] array2 = { };
System.Net.WebClient client = new System.Net.WebClient();
System.Net.WebClient client2 = new System.Net.WebClient();
client.Headers.Add("key-price", "application/json");
client2.Headers.Add("item-data", "application/json");
//get the JSON data.
string test = Encoding.ASCII.GetString(client.UploadData("http://backpack.tf/api/IGetCurrencies/v1/?key=54972a10b88d885f748b4956&appid=440&compress=1", "POST", Encoding.Default.GetBytes(array1)));
string currencyJSON = Encoding.ASCII.GetString(client2.UploadData("http://backpack.tf/api/IGetPrices/v4/?key=54972a10b88d885f748b4956&appid=440&compress=1", "POST", Encoding.Default.GetBytes(array2)));
//deserialize json data and store it in rootObject.cs
rootObject obj = JsonConvert.DeserializeObject<rootObject>(test);
//same as above but store itt in currencyData.cs
currencyData currencyData = JsonConvert.DeserializeObject<currencyData>(currencyJSON);
Response response = obj.response;
CurrencyResponse currencyResponse = currencyData.currencyResponse;
//check if the API responds, If not we can display an error message
if (response.success == 1 ) {
foreach (KeyValuePair<string, Currency> kvp in response.currencies)
{
string currencyName = kvp.Key;
Currency currency = kvp.Value;
}
foreach (KeyValuePair<string, CurrencyPrices> currencyDataDict in currencyResponse.items)
{
string itemName = currencyDataDict.Key;
CurrencyPrices currencyPrices = currencyDataDict.Value;
}
Currency kek = new Currency();
outputBox.Text = test;
}
rootObject:
class rootObject
{
public Response response { get; set; }
}
public class Response
{
public int success { get; set; }
public int current_time { get; set; }
public Dictionary<string, Currency> currencies { get; set; }
public string name { get; set; }
public string url { get; set; }
}
public class Currency
{
public int quality { get; set; }
public int priceindex { get; set; }
public string single { get; set; }
public string plural { get; set; }
public int round { get; set; }
public string craftable { get; set; }
public string tradable { get; set; }
public int active { get; set; }
public int defindex { get; set; }
}
Now my problem is the data isnt being retrieved in the second API call and if i remove the second foreach loop it will output the variable Test which contains JSON data, However if i keep the second foreach loop in it will print nothing..
Thanks in advance and sorry for the bad wording etc.
Backpack.tf API doccumentation
There are many problems with currencyData:
The JSON returned does not actually match the documentation (good job there Backpack.tf):
1.1. The field named "Untradable" is actually returned as "Non-Tradable"
1.2. The field named "Uncraftable" is actually returned as "Non-Craftable".
1.3. The "Craftable" objects are supposed to be returned in a dictionary of quantity to price data. In fact, they are sometimes returned as an array:
"Craftable": [
{
"currency": "metal",
"value": 1.33,
"last_update": 1417451879,
"difference": -0.11
}
]
But they are *sometimes* returned as a dictionary!
"Craftable": {
"10": {
"currency": "usd",
"value": 101.49,
"value_high": 124.04,
"last_update": 1362682641,
"difference": 34.719
},
"11": {
"currency": "earbuds",
"value": 1.4,
"last_update": 1406474947,
"difference": 31.236,
"value_high": 1.8
},
You have several fields declared as int which can have fractional data. They need to be changed to something else, for instance decimal.
Your data model does not match the documented model, which is several levels deeper than yours.
The following code and objects for currencyData read both the JSON shown in the documentation and the JSON actually returned by the call, since I suppose both must be handled. Note also the following qualification from the documentation:
Only one request can be made to this API per minute per API key. Additionally, the response is cached and updated every 10 minutes.
So, make sure you're not getting prices too often.
[DataContract]
public class currencyData
{
[DataMember(Name="response")]
public CurrencyResponse response { get; set; }
}
[DataContract]
public class CurrencyResponse
{
public CurrencyResponse()
{
this.items = new Dictionary<string,ItemPrices>();
}
[DataMember]
public int success { get; set; }
[DataMember]
public long current_time { get; set; }
[DataMember]
public decimal raw_usd_value { get; set; }
[DataMember]
public string usd_currency { get; set; }
[DataMember]
public long usd_currency_index { get; set; }
[DataMember(EmitDefaultValue = false)]
public Dictionary<string, ItemPrices> items { get; set; }
}
[DataContract]
public class ItemPrices
{
public ItemPrices()
{
this.prices = new Dictionary<long, ItemTradablePrices>();
}
[DataMember(EmitDefaultValue = false)]
public Dictionary<long, ItemTradablePrices> prices { get; set; }
}
[DataContract]
public class ItemTradablePrices
{
[DataMember(EmitDefaultValue = false)]
public ItemCraftablePrices Tradable { get; set; }
// Sometimes appears as "Non-Tradable", sometimes "Untradable". Handle both
[DataMember(EmitDefaultValue = false)]
public ItemCraftablePrices Untradable { get; set; }
[DataMember(Name = "Non-Tradable", EmitDefaultValue=false)]
ItemCraftablePrices NonTradable
{
get
{
return null;
}
set
{
Untradable = value;
}
}
}
[DataContract]
public class ItemCraftablePrices
{
[DataMember(EmitDefaultValue = false)]
[JsonConverter(typeof(PrinceIndexDictionaryConverter))]
public Dictionary<long, PriceIndex> Craftable { get; set; }
// Sometimes appears as "Non-Craftable", sometimes "Uncraftable". Handle both
[DataMember(EmitDefaultValue=false)]
[JsonConverter(typeof(PrinceIndexDictionaryConverter))]
public Dictionary<long, PriceIndex> Uncraftable { get; set; }
[DataMember(Name="Non-Craftable", EmitDefaultValue=false)]
[JsonConverter(typeof(PrinceIndexDictionaryConverter))]
Dictionary<long, PriceIndex> NonCraftable
{
get
{
return null;
}
set
{
Uncraftable = value;
}
}
}
public class PrinceIndexDictionaryConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Dictionary<long, PriceIndex>);
}
public override bool CanWrite
{
get
{
return false;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var dict = existingValue as Dictionary<long, PriceIndex>;
if (dict == null)
dict = new Dictionary<long, PriceIndex>();
switch (reader.TokenType)
{
case JsonToken.StartArray:
List<PriceIndex> list = new List<PriceIndex>();
serializer.Populate(reader, list);
for (int i = 0; i < list.Count; i++)
dict[i] = list[i];
break;
case JsonToken.StartObject:
serializer.Populate(reader, dict);
break;
default:
Debug.WriteLine("Unexpected token type " + reader.TokenType.ToString());
throw new InvalidOperationException();
}
return dict;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class PriceIndex
{
public string currency { get; set; }
public decimal value { get; set; }
public decimal value_high { get; set; }
public decimal value_raw { get; set; }
public decimal value_high_raw { get; set; }
public long last_update { get; set; }
public decimal difference { get; set; }
}
Then you could use it like:
public currencyData SendCurrencyQuery()
{
//make call to the API and retrieve JSON data
char[] array2 = { };
using (var client2 = new System.Net.WebClient())
{
client2.Headers.Add("item-data", "application/json");
//get the JSON data.
string currencyJSON = Encoding.UTF8.GetString(client2.UploadData("http://backpack.tf/api/IGetPrices/v4/?key=54972a10b88d885f748b4956&appid=440&compress=1", "POST", Encoding.UTF8.GetBytes(array2)));
//same as above but store itt in currencyData.cs
var currencyData = JsonConvert.DeserializeObject<currencyData>(currencyJSON);
return currencyData;
}
}

Serialize deserialize anonymous child JSON properties to model

I have an API I am receiving data from. That API is out of my control on how it is structured, and I need to serialize and deserialize the JSON output to map the data to my model.
Everything works well where JSON is nicely formatted with named properties.
What can you do where there is no named value and there is just an array of ints and strings? like under locations
here is a sample of the JSON:
{"id":"2160336","activation_date":"2013-08-01","expiration_date":"2013-08-29","title":"Practice Manager","locations":{"103":"Cambridge","107":"London"}}
I have models that are like:
public class ItemResults
{
public int Id { get; set; }
public DateTime Activation_Date { get; set; }
public DateTime Expiration_Date{ get; set; }
public string Title { get; set; }
public Location Locations { get; set; }
}
public class Location
{
public int Id { get; set; }
public string value { get; set; }
}
and I am mapping using the inbuilt ajax serialization:
protected T MapRawApiResponseTo<T>( string response )
{
if ( string.IsNullOrEmpty( response ) )
{
return default( T );
}
var serialize = new JavaScriptSerializer();
return serialize.Deserialize<T>( response );
}
var results = MapRawApiResponseTo<ItemResults>(rawApiResponse);
So the ID and all other properties are picked up and mapped but what every I do I can not seem to map the locations.
Many thanks
public Dictionary<int,string> Locations { get; set; }
job done; you should find that using Json.NET, at least, i.e.
var result = JsonConvert.DeserializeObject<ItemResults>(json);
you get 2 entries in result.Locations; specifically result[103] = "Cambridge"; and result[107] = "London";
If you don't mind, you can workaround with dictionary:
class Program
{
static void Main(string[] args)
{
string json =
"{'id':'2160336','activation_date':'2013-08-01','expiration_date':'2013-08-29','title':'Practice Manager','locations':{'103':'Cambridge','107':'London'}}";
var deserializeObject = JsonConvert.DeserializeObject<ItemResults>(json);
Console.WriteLine("{0}:{1}", deserializeObject.Locations.First().Key, deserializeObject.Locations.First().Value);
Console.ReadKey();
}
}
public class ItemResults
{
public int Id { get; set; }
public DateTime Activation_Date { get; set; }
public DateTime Expiration_Date { get; set; }
public string Title { get; set; }
public Dictionary<int, string> Locations { get; set; }
}
you can also use manual parsing, like here: Json.NET (Newtonsoft.Json) - Two 'properties' with same name?
This will work:
public Dictionary<string, string> Locations { get; set; }
public IEnumerable<Location> LocationObjects { get { return Locations
.Select(x => new Location { Id = int.Parse(x.Key), value = x.Value }); } }
I propose you the following solution :
public class ItemResults
{
public int Id { get; set; }
public DateTime Activation_Date { get; set; }
public DateTime Expiration_Date { get; set; }
public string Title { get; set; }
[JsonProperty("locations")]
public JObject JsonLocations { get; set; }
[JsonIgnore]
public List<Location> Locations { get; set; }
[OnDeserialized]
public void OnDeserializedMethod(StreamingContext context)
{
this.Locations = new List<Location>();
foreach (KeyValuePair<string, JToken> item in this.JsonLocations)
{
this.Locations.Add(new Location() { Id = int.Parse(item.Key), value = item.Value.ToString() });
}
}
}
public class Location
{
public int Id { get; set; }
public string value { get; set; }
}
After you just have to deserialize your JSON with : JsonConvert.DeserializeObject<ItemResults>(json);

Categories

Resources