Cant store this JSON data in c#? - 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;
}
}

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(),
});

Deserialize JSON array which has mixed values System.Text.JSON

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 });

C# data contract for complex JSON object

This is probably something really simple and I looked everywhere and tried everything I could come up with. So I apologize if this is a simple search and I was just looking for the wrong thing. I'm also new to data contracts and somewhat JSON so this probably isn't really that complex.
I am creating an API to ingest JSON and store it in our database.
The JSON will look something like this:
{
"appname" : "MyApp",
"key" : "Test123",
"data" :[
{ "data1" : "10551296", "data2" : "TrainingIns", "data3" : "Completed"}
,
{ "connectorType" : "webserver-to-appserver", "sourceUri" : "data4", "destinationUri" : "data5", "rails" : "N", "data6" : "null" }
,
{ "groupId" : "group1", "failbackAction" : "null", "normal" : "null", "failoverAction" : "null", "failbackAction" : "null", "failoverAction" : "null", "artifactId" : "mywebserver", "normalState" : "null" }
,
{ "instanceId" : "10551296abc" }]
,
"updateId" : "MyID",
"updateTS" : "30-AUG-16 05.56.24.000000000 AM" ,
"creationUser" : "APICall"
}
Where the 'data' field will be an array with a variable amount of JSON objects. The issue I am having stems from either not getting data in the 'data' object or having it be completely undefined.
[DataContract]
public class Update_DB
{
[DataMember(Name = "appname", IsRequired = true)]
public string appname { get; set; }
[DataMember]
public string key { get; set; }
[DataMember(Name="data",IsRequired = true)]
public List<JsonValue> data { get; set; }
[DataMember]
public string updateId { get; set; }
[DataMember]
public string updateTS { get; set; }
[DataMember]
public string creationUser { get; set; }
}
I've gathered I might need a collection of some sort? I've tried everything I could find but I don't know how I should define the data member for 'data'.
The above contract gives me empty arrays when I do this:
string x = JsonConvert.SerializeObject(collection.data);
I can get every other field I just need to turn the 'data' field into a string.
Hopefully that is enough info. Thanks in advance for any help!
Under normal circumstances, you could define your data property as a List<Dictionary<string, string>>, like so:
[DataMember(Name = "data", IsRequired = true)]
public List<Dictionary<string, string>> data { get; set; }
Then you would be able to serialize and deserialize it successfully with Json.NET. Unfortunately, one of your data objects has duplicated keys:
{
"groupId":"group1",
"failbackAction":"null",
"normal":"null",
"failoverAction":"null",
"failbackAction":"null",
"failoverAction":"null",
"artifactId":"mywebserver",
"normalState":"null"
},
Using duplicated keys is not recommended by the JSON standard, which states:
When the names within an object are not unique, the behavior of software that receives such an object is unpredictable.
In addition, c# dictionaries of course do not support duplicated keys, and data contract serialization does not duplicated property names.
However, it is possible to read a JSON object with duplicated keys using Json.NET's JsonReader and create a custom JsonConverter to handle duplicated keys.
First, define the following class to replace JsonValue. JsonValue is a silverlight-specific class whose use has been deprecated in overall .Net:
[JsonConverter(typeof(JsonValueListConverter))]
public sealed class JsonValueList
{
public JsonValueList()
{
this.Values = new List<KeyValuePair<string, string>>();
}
public List<KeyValuePair<string, string>> Values { get; private set; }
}
class JsonValueListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(JsonValueList).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jsonValue = (existingValue as JsonValueList ?? new JsonValueList());
if (reader.TokenType != JsonToken.StartObject)
throw new JsonSerializationException("Invalid reader.TokenType " + reader.TokenType);
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.Comment:
break;
case JsonToken.PropertyName:
{
var key = reader.Value.ToString();
if (!reader.Read())
throw new JsonSerializationException(string.Format("Missing value at path: {0}", reader.Path));
var value = serializer.Deserialize<string>(reader);
jsonValue.Values.Add(new KeyValuePair<string, string>(key, value));
}
break;
case JsonToken.EndObject:
return jsonValue;
default:
throw new JsonSerializationException(string.Format("Unknown token {0} at path: {1} ", reader.TokenType, reader.Path));
}
}
throw new JsonSerializationException(string.Format("Unclosed object at path: {0}", reader.Path));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var jsonValue = (JsonValueList)value;
writer.WriteStartObject();
foreach (var pair in jsonValue.Values)
{
writer.WritePropertyName(pair.Key);
writer.WriteValue(pair.Value);
}
writer.WriteEndObject();
}
}
Notice the use of [JsonConverter(typeof(JsonValueListConverter))]. This specifies the use of a custom converter when serializing and deserializing JsonValueList.
Next, define your Update_DB class as follows:
[DataContract]
public class Update_DB
{
[DataMember(Name = "appname", IsRequired = true)]
public string appname { get; set; }
[DataMember]
public string key { get; set; }
[DataMember(Name = "data", IsRequired = true)]
public List<JsonValueList> data { get; set; }
[DataMember]
public string updateId { get; set; }
[DataMember]
public string updateTS { get; set; }
[DataMember]
public string creationUser { get; set; }
}
Now you will be able to serialize and deserialize your JSON successfully. Sample fiddle.
Update
If you do not have duplicated keys, you can define your class as follows:
[DataContract]
public class Update_DB
{
[DataMember(Name = "appname", IsRequired = true)]
public string appname { get; set; }
[DataMember]
public string key { get; set; }
[DataMember(Name = "data", IsRequired = true)]
public List<Dictionary<string, string>> data { get; set; }
[DataMember]
public string updateId { get; set; }
[DataMember]
public string updateTS { get; set; }
[DataMember]
public string creationUser { get; set; }
}
And then the following:
var collection = new Update_DB
{
data = new List<Dictionary<string, string>>
{
new Dictionary<string, string>
{
{"data1", "10551296"},
{"data2", "TrainingIns"},
{"data3", "Completed"},
},
new Dictionary<string, string>
{
{"connectorType", "webserver-to-appserver"},
{"sourceUri", "data4"},
{"destinationUri", "data5"},
},
},
};
string x = JsonConvert.SerializeObject(collection.data, Formatting.Indented);
Console.WriteLine(x);
Produces the output:
[
{
"data1": "10551296",
"data2": "TrainingIns",
"data3": "Completed"
},
{
"connectorType": "webserver-to-appserver",
"sourceUri": "data4",
"destinationUri": "data5"
}
]
Sample fiddle.
Another option is to use the dynamic keyword. You could use a list of this type for data (per below).
[DataContract]
public class Update_DB
{
[DataMember(Name = "appname", IsRequired = true)]
public string appname { get; set; }
[DataMember]
public string key { get; set; }
[DataMember(Name = "data", IsRequired = true)]
public List<dynamic> data { get; set; }
[DataMember]
public string updateId { get; set; }
[DataMember]
public string updateTS { get; set; }
[DataMember]
public string creationUser { get; set; }
}
From there, you could use the object by deserializing with JSON.Net, and access into the dynamic data object (assuming you know something about the shape of this dynamic object). Something like below will work based on the input string from the original post.
Update_DB dataObj = JsonConvert.DeserializeObject<Update_DB>(objectJSON);
string test = dataObj.data[1].connectorType; //evaluates to "webserver-to-appserver"
Use Json2CSharp.com to make sure you have everything correct:
public class Datum
{
public string data1 { get; set; }
public string data2 { get; set; }
public string data3 { get; set; }
public string connectorType { get; set; }
public string sourceUri { get; set; }
public string destinationUri { get; set; }
public string rails { get; set; }
public string data6 { get; set; }
public string groupId { get; set; }
public string failbackAction { get; set; }
public string normal { get; set; }
public string failoverAction { get; set; }
public string artifactId { get; set; }
public string normalState { get; set; }
public string instanceId { get; set; }
}
public class RootObject
{
public string appname { get; set; }
public string key { get; set; }
public List<Datum> data { get; set; }
public string updateId { get; set; }
public string updateTS { get; set; }
public string creationUser { get; set; }
}

JSON to C# object

I have a problem with JSON.
"{"status":"ok","message":"Dane klienta zostau0142y pobrane pomyu015blnie","clientData":
{"id":22,"imie":"Pppppppppp","nazwisko":"Ppppppppppppp","tel":"111111126","email":"aaa#a.pl","ulica":"Na Przyzbie","nr_budynku":"3","nr_lokalu":"41","kod_pocztowy":"02-813","miejscowosc":"Warszawa","samochod_marka":"opel","samochod_model":"vectra","subcategories":
{"6":200}}}"
and it's my class
public class Client
{
public string Status { get; set; }
public string Message { get; set; }
public Data clientData { get; set; }
}
public class Data
{
public Dictionary<string, string> clientData { get; set; }
}
everything is mostly correct but when I debug my code field clientData is null.
What am I doing wrong?
Thanks for help!
EDIT:
it's how I deserialize object.
var myObject = JsonConvert.DeserializeObject<Client>(get_person);
The problem with your current attempt is that you are trying to convert clientData to a Dictionary<string, string>. This is causing an issue because not all of your values are strings, the problematic ones are as follows:
id : int
subcategories : Dictionary<string, int>
If you don't want to explicitly define all of your properties due to them changing without notice, then I would recommend a change to your JSON structure as follows:
{
"status": "ok",
"message": "Dane klienta zostau0142y pobrane pomyu015blnie",
"clientData": {
"id": 22,
"properties": {
"imie": "Pppppppppp",
"nazwisko": "Ppppppppppppp",
"tel": "111111126",
"email": "aaa#a.pl",
"ulica": "Na Przyzbie",
"nr_budynku": "3",
"nr_lokalu": "41",
"kod_pocztowy": "02-813",
"miejscowosc": "Warszawa",
"samochod_marka": "opel",
"samochod_model": "vectra"
},
"subcategories": {
"6": 200
}
}
}
Then you change your C# class structure to the following:
public class Client
{
public string Status { get; set; }
public string Message { get; set; }
public Data clientData { get; set; }
}
public class Data
{
public int id { get; set;}
public Dictionary<string, string> properties { get; set; }
public Dictionary<string, int> subcategories { get; set; }
}
That should work (though I haven't tested), and will hopefully allow you to use it how you need to still.
NOTE: You could also move id and subcategories into the root, and keep clientData as a Dictionary<string, string>. All depends on your preference really, the important thing here is that you be careful not to mix types.
Json
{
"status":"ok",
"message":"Dane klienta zostau0142y pobrane pomyu015blnie",
"clientData":{
"id":22,
"imie":"Pppppppppp",
"nazwisko":"Ppppppppppppp",
"tel":"111111126",
"email":"aaa#a.pl",
"ulica":"Na Przyzbie",
"nr_budynku":"3",
"nr_lokalu":"41",
"kod_pocztowy":"02-813",
"miejscowosc":"Warszawa",
"samochod_marka":"opel",
"samochod_model":"vectra",
"subcategories":{
"6":200
}
}
}
C# classes
public class Subcategories
{
public int __invalid_name__6 { get; set; }
}
public class ClientData
{
public int id { get; set; }
public string imie { get; set; }
public string nazwisko { get; set; }
public string tel { get; set; }
public string email { get; set; }
public string ulica { get; set; }
public string nr_budynku { get; set; }
public string nr_lokalu { get; set; }
public string kod_pocztowy { get; set; }
public string miejscowosc { get; set; }
public string samochod_marka { get; set; }
public string samochod_model { get; set; }
public Subcategories subcategories { get; set; }
}
public class RootObject
{
public string status { get; set; }
public string message { get; set; }
public ClientData clientData { get; set; }
}
Note that root->clientData->subcategories->6 would result in invalid class name, as class names in C# can not begin with a number.
With hack fix:
For example:
public class DynamicDictionary : DynamicObject
{
private readonly Dictionary<string, object> dictionary;
public DynamicDictionary(Dictionary<string, object> dictionary)
{
this.dictionary = dictionary;
}
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
return dictionary.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
}
Which can be used as follows:
dynamic x = new DynamicDictionary(
new Dictionary<string, object> {{"Name", "Peter"}});
you can use Newtonsoft.Json - add a reference to your project and add the using directive
using Newtonsoft.Json;
//then your code
dynamic ma_json = JsonConvert.DeserializeObject<dynamic>(json);
//and then you can get say the id:
var id = ma_json.clientData.id;
// ... do whatever you want with the id
if (ma_json.clientData.id == 22) //evaluates to true in your case
{
//do something
}

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