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; }
}
Related
I'm querying an external service and wanted to deserialize the response into a customer object but the issue is response for each customer may be different. some customer may have Sales entity in the response and few may have Marketing.
The json property for sales entity is SalesId and for marketing is MarketingId. Can you advise whether the model I use to store result is correct or any improvement ? If so, how would I deserialize the response without knowing the correct json property ?
For Customer 66666
{
"customerId": "66666",
"customerName": "test1234",
"dependentEntity": [
{
"SalesId": "3433434",
"SalesPersonName": "343434",
"SaleSource": "StorePurchase"
}
]
}
For Customer 5555
{
"customerId": "55555",
"customerName": "test2",
"dependentEntity": [
{
"MarketingId": "3433434",
"MarketingAppName": "343434",
"MarketingSource": "Online"
}
]
}
Here is the Model I'm thinking but not sure the correct one
public class Customer
{
public string customerId { get; set; }
public string customerName { get; set; }
public IList<T> dependentList { get; set; }
}
public class Dependent
{
[JsonProperty("Id")]
public string Id { get; set; }
public string Name { get; set; }
public string Source { get; set; }
}
You could probably try something like the following one:
public class DependentEntity
{
[JsonProperty("SalesId")]
public string SalesId { get; set; }
[JsonProperty("SalesPersonName")]
public string SalesPersonName { get; set; }
[JsonProperty("SaleSource")]
public string SaleSource { get; set; }
[JsonProperty("MarketingId")]
public string MarketingId { get; set; }
[JsonProperty("MarketingAppName")]
public string MarketingAppName { get; set; }
[JsonProperty("MarketingSource")]
public string MarketingSource { get; set; }
}
public class Customer
{
[JsonProperty("customerId")]
public string CustomerId { get; set; }
[JsonProperty("customerName")]
public string CustomerName { get; set; }
[JsonProperty("dependentEntity")]
public IList<DependentEntity> DependentEntity { get; set; }
}
We have a type for DependentEntity that has both the attributes of Marketing and Sales object. After parsing your input, you could create a logic (checking the attributes) based on which you could check if a DependentEntity is a Marketing or a Sales object.
The above classes was generated using, jsonutils.
If we can assume that the dependentEntity contains only a single type of objects then you can use json.net's schema to perform branching based on the matching schema.
So, lets suppose you have these dependent entity definitions:
public class DependentMarket
{
public string MarketingId { get; set; }
public string MarketingAppName { get; set; }
public string MarketingSource { get; set; }
}
public class DependentSales
{
public string SalesId { get; set; }
public string SalesPersonName { get; set; }
[JsonProperty("SaleSource")]
public string SalesSource { get; set; }
}
...
Then you can use these classes to generate json schemas dynamically:
private static JSchema marketSchema;
private static JSchema salesSchema;
//...
var generator = new JSchemaGenerator();
marketSchema = generator.Generate(typeof(DependentMarket));
salesSchema = generator.Generate(typeof(DependentSales));
And finally you can do the branching like this:
var json = "...";
var semiParsedJson = JObject.Parse(json);
JArray dependentEntities = (JArray)semiParsedJson["dependentEntity"];
JObject probeEntity = (JObject)dependentEntities.First();
if (probeEntity.IsValid(marketSchema))
{
var marketEntities = dependentEntities.ToObject<List<DependentMarket>>();
...
}
else if (probeEntity.IsValid(salesSchema))
{
var salesEntities = dependentEntities.ToObject<List<DependentSales>>();
...
}
else if ...
else
{
throw new NotSupportedException("The provided json format is not supported");
}
So I'm making a small JSON Parser.
This is the JSON I want to parse:
{"158023":{"prices":{"xbox":{"LCPrice":"225,000","LCPrice2":"232,000","LCPrice3":"235,000","LCPrice4":"235,000","LCPrice5":"239,000","updated":"15 secs ago","MinPrice":"27,000","MaxPrice":"500,000","PRP":"41"},"ps":{"LCPrice":"228,000","LCPrice2":"231,000","LCPrice3":"232,000","LCPrice4":"233,000","LCPrice5":"235,000","updated":"9 mins ago","MinPrice":"30,000","MaxPrice":"550,000","PRP":"38"},"pc":{"LCPrice":"305,000","LCPrice2":"305,000","LCPrice3":"315,000","LCPrice4":"333,000","LCPrice5":"347,000","updated":"1 hour ago","MinPrice":"37,000","MaxPrice":"700,000","PRP":"40"}}}}
And I have the following class, to represent the Json Object.
public partial class Prices
{
[JsonProperty("158023")]
public Token TokenNumber { get; set; }
}
public partial class Token
{
[JsonProperty("prices")]
public PricesClass Prices { get; set; }
}
public partial class PricesClass
{
[JsonProperty("xbox")]
public Pc Xbox { get; set; }
[JsonProperty("ps")]
public Pc Ps { get; set; }
[JsonProperty("pc")]
public Pc Pc { get; set; }
}
public partial class Pc
{
[JsonProperty("LCPrice")]
public string LcPrice { get; set; }
[JsonProperty("LCPrice2")]
public string LcPrice2 { get; set; }
[JsonProperty("LCPrice3")]
public string LcPrice3 { get; set; }
[JsonProperty("LCPrice4")]
public string LcPrice4 { get; set; }
[JsonProperty("LCPrice5")]
public string LcPrice5 { get; set; }
[JsonProperty("updated")]
public string Updated { get; set; }
[JsonProperty("MinPrice")]
public string MinPrice { get; set; }
[JsonProperty("MaxPrice")]
public string MaxPrice { get; set; }
[JsonProperty("PRP")]
public string Prp { get; set; }
}
public partial class Prices
{
public static Prices FromJson(string json) => JsonConvert.DeserializeObject<Prices>(json, PriceConverter.Settings);
}
internal static class PriceConverter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters = {
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
I'm easily able to parse the JSON, by doing:
Prices prices = Prices.FromJson(myJson);
The problem is when I want to use a different number than 158023.
For example, 158025.
The JsonProperty on the Prices class is already set to "158023", and I have no clue how to rename it.
TLDR:
I have a JSON Object, which I want to rename the JsonProperty text, before deserializing.
Since you don't know the key, use a Dictionary<string, Token> instead of the property TokenNumber in class Prices.
public partial class Prices
{
// Remove this property
// [JsonProperty("158023")]
// public Token TokenNumber { get; set; }
}
public partial class Prices
{
public static Dictionary<string, Token> FromJson(string json) => JsonConvert.DeserializeObject<Dictionary<string, Token>>(json, PriceConverter.Settings);
}
Now the result will be a dictionary where the key is the token value as an string and the value is a Token object.
You could make use of the JsonExtensionData and OnDeserialized attributes:
public class Wrapper
{
public string Id { get; set; }
public Item Item { get; set; }
[JsonExtensionData]
private IDictionary<string, JToken> _additionalData;
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
// Get the first key. If you have more than one, you may need
// to customize this for your use case
var id = _additionalData.Keys.FirstOrDefault();
if (id != null)
{
// Assign to our Id property
this.Id = id;
// Create a reader for the subobject
var itemReader = _additionalData[id].CreateReader();
var serializer = new JsonSerializer();
// Deserialize the subobject into our Item property
this.Item = serializer.Deserialize<Item>(itemReader);
itemReader.Close();
}
}
}
public class Item
{
public string Name { get; set; }
}
You can try it here. Alternatively you could write a JsonConverter to achieve the same thing, or do what Amir suggested.
I am getting JSon from a third party API which does not match my classes.
Some JSon properties are not be be converted others has different names, etc.
How can I define a custom conversion from the JSON to my C# object.
These are the objects I have in C#:
public class PropertyList {
public Int32 Page { get; set; }
public String[] ErrorMessages { get; set; }
public List<Property> Properties { get; set; }
}
public class Property {
public String Name { get; set; }
public String Reference { get; set; }
public String Region { get; set; }
public IList<String> Features { get; set; }
public String Id { get; set; }
public Double Price { get; set; }
public IList<String> ImagesUrls { get; set; }
}
And this is the JSon data which I want to convert from:
{
"page" : 0,
"errorMessages" : [ ],
"listings" : [
{
"data" : {
"name" : "The name",
"reference__c" : "ref1234",
"region__c" : "London",
"features__c" : "Garage;Garden;",
"id" : "id1234",
"price_pb__c" : 700000,
},
"media" : {
"images" : [
{
"title" : "image1",
"url" : "http://www.domain.com/image1"
},
{
"title" : "image2",
"url" : "http://www.domain.com/image2"
}
]
}
}
{
NOTE: Other items
}
]
}
How should I do this?
UPDATE 1
Using Thiago suggestion I was able to parse Page and ErrorMessages but not Properties. So I create the following converter:
public class PropertyResultConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return (objectType == typeof(PropertyResult));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
JObject jo = JObject.Load(reader);
PropertyResult propertyResult = jo.ToObject<PropertyResult>();
propertyResult.Properties = jo.SelectToken("listings.data").ToObject<List<Property>>();
return propertyResult;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
throw new NotImplementedException();
}
}
But when I try this I get the following error:
An exception of type 'System.NullReferenceException' occurred
On:
propertyResult.Properties = jo.SelectToken("listings.data").ToObject<List<Property>>();
Any idea why this happens?
Miguel, have you tried http://www.newtonsoft.com/json ?
You can do the mappings of your code using C# attributes, like the snippets below.
public class PropertyList {
[JsonProperty("page")]
public Int32 Page { get; set; }
[JsonProperty("errorMessages")]
public String[] ErrorMessages { get; set; }
[JsonProperty("listings")]
public List<Property> Properties { get; set; }
}
public class Property {
[JsonProperty("name")]
public String Name { get; set; }
[JsonProperty("reference__c")]
public String Reference { get; set; }
[JsonProperty("region__c")]
public String Region { get; set; }
[JsonProperty("features__c")]
public IList<String> Features { get; set; }
[JsonProperty("id")]
public String Id { get; set; }
[JsonProperty("price_pb__c")]
public Double Price { get; set; }
[JsonProperty("media")]
public IList<String> ImagesUrls { get; set; }
}
I'm not sure if IList will work as you are expecting. I believe you will need to adapt it to Dictionary.
You can take a look on the API:
http://www.newtonsoft.com/json/help/html/SerializationAttributes.htm
UPDATE
Try to parse 'data' using the snippet below:
JObject jsonObject = JObject.Parse(json);
IList<JToken> results = jsonObject["listings"]["data"].Children().ToList();
IList<Property> processedList = new List<Property>();
foreach (JToken item in results)
{
Property propertyItem = JsonConvert.DeserializeObject<Property>(item.ToString());
processedList.Add(propertyItem);
}
I ran that snippet and it worked. You can explore the code that I generated on my github:
https://github.com/thiagoavelino/VisualStudio_C/blob/master/VisualStudio_C/StackOverFlow/ParsingJason/ParsingJson.cs
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;
}
}
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
}