I'm trying to work with AWS SDK in C# to follow and update a price catalog.
I'm using the method GetProductsAsync to list EC2 products for example, I then try to deserialize the response.
I use Json.Net to deserialize my response into a class I created using the "Paste JSON as classes" function from Visual Studio.
The object is somewhat populated, but the pricing model follows a weird JSON pattern.
Here is an extract of the object:
"terms":{
"OnDemand":{
"FBKCX9C4KX8NSVN3.JRTCKXETXF":{
"priceDimensions":{
"FBKCX9C4KX8NSVN3.JRTCKXETXF.6YS6EN2CT7":{
"unit":"Hrs",
"endRange":"Inf",
"description":"$2.47 per On Demand RHEL m4.10xlarge Instance Hour",
"appliesTo":[
],
"rateCode":"FBKCX9C4KX8NSVN3.JRTCKXETXF.6YS6EN2CT7",
"beginRange":"0",
"pricePerUnit":{
"USD":"2.4700000000"
}
}
},
The IDs under OnDemand and PriceDimensions seem to be references to other objects; therefore, they are not populated when I deserialize the JSON object, as they are different per product type.
Has anyone succeeded in getting pricing information for AWS assets?
For JSON objects having keys which can vary, you can use a Dictionary<string, T> in place of a regular class, where T is a class representing the item data. So in your case, you'd need a dictionary for both OnDemand and priceDimensions. The resulting class definitions would look like this:
public class OuterObject
{
public Terms terms { get; set; }
}
public class Terms
{
public Dictionary<string, OnDemandItem> OnDemand { get; set; }
}
public class OnDemandItem
{
public Dictionary<string, PriceDimensionsItem> priceDimensions { get; set; }
}
public class PriceDimensionsItem
{
public string unit { get; set; }
public string endRange { get; set; }
public string description { get; set; }
public object[] appliesTo { get; set; }
public string rateCode { get; set; }
public string beginRange { get; set; }
public PricePerUnit pricePerUnit { get; set; }
}
public class PricePerUnit
{
public string USD { get; set; }
}
Demo: https://dotnetfiddle.net/dJ5jmQ
Note: you could also use a Dictionary<string, string> in place of the PricePerUnit class if you will be dealing with a lot of different currencies. If there will just one or two, then having a strongly-typed class with properties for each possible currency will work fine. For example, you could add a property public string EUR { get; set; } to handle Euro.
AWS SDK have one more "level" after OnDemand.
I made my own class based on Brian Rogers answer, and i add the rest of the class to support Reserved and Products, making a nested class.
public class OuterObject
{
public Dictionary<string, Products> products { get; set; }
public Terms terms { get; set; }
}
public class Products
{
public string sku { get; set; }
public string productFamily { get; set; }
public Attributes attributes { get; set; }
}
public class Attributes
{
public string servicecode { get; set; }
public string location { get; set; }
public string locationType { get; set; }
public string instanceType { get; set; }
public string currentGeneration { get; set; }
public string vcpu { get; set; }
public string memory { get; set; }
public string operatingSystem { get; set; }
public string licenseModel { get; set; }
public string preInstalledSw { get; set; }
public string tenancy { get; set; }
}
public class Terms
{
public Dictionary<string, Dictionary<string, OnDemandItem>> OnDemand { get; set; }
public Dictionary<string, Dictionary<string, OnDemandItem>> Reserved { get; set; }
}
public class OnDemandItem
{
public string offerTermCode { get; set; }
public string sku { get; set; }
public Dictionary<string, PriceDimensionsItem> priceDimensions { get; set; }
public TermAttributes termAttributes { get; set; }
}
public class PriceDimensionsItem
{
public string unit { get; set; }
public string endRange { get; set; }
public string description { get; set; }
public object[] appliesTo { get; set; }
public string rateCode { get; set; }
public string beginRange { get; set; }
public PricePerUnit pricePerUnit { get; set; }
}
public class PricePerUnit
{
public string USD { get; set; }
}
public class TermAttributes
{
public string LeaseContractLength { get; set; }
public string OfferingClass { get; set; }
public string PurchaseOption { get; set; }
}
Related
I currently have JSON coming in as follows:
{"36879":[{"min_qty":1,"discount_type":"%","csp_price":10}],"57950":[{"min_qty":1,"discount_type":"flat","csp_price":650}]}
This contains a list of the following records
ProductId
MinQty
DiscountType
Price
I need to deserialize this into the following model:
public class CustomerSpecificPricing
{
string productId { get; set; }
public virtual List<CustomerSpecificPricingDetail> CustomerSpecificPricingDetails { get; set; }
}
public class CustomerSpecificPricingDetail
{
public string min_qty { get; set; }
public string discount_type { get; set; }
public string csp_price { get; set; }
}
The problem is that the "productId" of each record is missing the key name.
If I run my JSON through J2C, I get the following:
public class 36879 {
public int min_qty { get; set; }
public string discount_type { get; set; }
public int csp_price { get; set; }
}
public class 57950 {
public int min_qty { get; set; }
public string discount_type { get; set; }
public int csp_price { get; set; }
}
public class Root {
public List<_36879> _36879 { get; set; }
public List<_57950> _57950 { get; set; }
}
Which is obviously incorrect.
How would I deserialize my object correctly?
You would need to deserialize it into a dictionary first and then map it into the format you require after. Something like this should work:
var dict = JsonConvert.DeserializeObject<Dictionary<string, IEnumerable<CustomerSpecificPricingDetail>>>();
var result = dict.Select(kvp => new CustomerSpecificPricing { ProductId = Int32.Parse(kvp.Key), CustomerSpecificPricingDetails = kvp.Value });
Id also recommend you follow the conventional standards of naming. In this case properties in classes should be PascalCase,
e.g. your classes now become:
public class CustomerSpecificPricing
{
[JsonProperty("productId ")]
public string ProductId { get; set; }
public virtual List<CustomerSpecificPricingDetail> CustomerSpecificPricingDetails { get; set; }
}
and
public class CustomerSpecificPricingDetail
{
[JsonProperty("min_qty")]
public string MinQty { get; set; }
[JsonProperty("discount_type ")]
public string DiscountType { get; set; }
[JsonProperty("csp_price ")]
public string CspPrice { get; set; }
}
I have an Api returning JSON data as response. This response is basically answers to a survey taken. We have multiple surveys. Each survey will have multiple questions which can be different from the other surveys. (Hope I make sense here)
I have created a model class from the response I get for one of the survey like follows :-
public string value1 { get; set; }
public string value2 { get; set; }
public string value3 { get; set; }
public string value4 { get; set; }
public int FileSystemObjectType { get; set; }
public int Id { get; set; }
public object someUrl1 { get; set; }
public string SomeUrl2 { get; set; }
public string TypeId { get; set; }
public string DifferentValue1 { get; set; }
public bool DifferentValue2 { get; set; }
public List<string> DifferentValue3 { get; set; }
public List<string> DifferentValue4 { get; set;
The properties DifferentValue1, DifferentValue2, DifferentValue3 .. are the questions that can be different for each survey. Right now this survey has 4 questions, but other surveys can have different number of questions. The rest of the properties remain same throughout the surveys.
Eg. Other survey can have a model
public string value1 { get; set; }
public string value2 { get; set; }
public string value3 { get; set; }
public string value4 { get; set; }
public int FileSystemObjectType { get; set; }
public int Id { get; set; }
public object someUrl1 { get; set; }
public string SomeUrl2 { get; set; }
public string TypeId { get; set; }
public string DifferentValue1 { get; set; }
public bool DifferentValue2 { get; set; }
Is there a way I can make a generic model class where the properties for the question can be different but other properties are same?
Any help is appreciated.
Sounds to me like a typical scenario for inheritance..
Create a base class with properties that you want to have for all surveys and then derive from that class and add properties for the individual questions to that child class.
Example:
public class SurveyBase
{
public int Id { get; set; }
public string SomeUrl { get; set; }
public string TypeId { get; set; }
//...
}
public class Survey1 : SurveyBase
{
public string MyQuestion1 { get; set; }
public string MyQuestion2 { get; set; }
//..
}
public class Survey2 : SurveyBase
{
public string OtherProperty { get; set; }
public int Whatever { get; set; }
//..
}
So I created a class using json2csharp
public class ResponseType
{
public class Query
{
public string q { get; set; }
public object sku { get; set; }
public int limit { get; set; }
public object reference { get; set; }
public object mpn_or_sku { get; set; }
public string mpn { get; set; }
public object brand { get; set; }
public string __class__ { get; set; }
public int start { get; set; }
public object seller { get; set; }
}
public class Request
{
public bool exact_only { get; set; }
public string __class__ { get; set; }
public List<Query> queries { get; set; }
}
public class Seller
{
public string display_flag { get; set; }
public bool has_ecommerce { get; set; }
public string name { get; set; }
public string __class__ { get; set; }
public string homepage_url { get; set; }
public string id { get; set; }
public string uid { get; set; }
}
public class Prices
{
public List<List<object>> USD { get; set; }
public List<List<object>> JPY { get; set; }
public List<List<object>> CNY { get; set; }
}
public class Offer
{
public string sku { get; set; }
public string packaging { get; set; }
public string on_order_eta { get; set; }
public string last_updated { get; set; }
public int? order_multiple { get; set; }
public int in_stock_quantity { get; set; }
public string eligible_region { get; set; }
public int? moq { get; set; }
public int? on_order_quantity { get; set; }
public object octopart_rfq_url { get; set; }
public string __class__ { get; set; }
public Seller seller { get; set; }
public string product_url { get; set; }
public object factory_order_multiple { get; set; }
public string _naive_id { get; set; }
public int? factory_lead_days { get; set; }
public Prices prices { get; set; }
public bool is_authorized { get; set; }
public bool is_realtime { get; set; }
}
public class Brand
{
public string homepage_url { get; set; }
public string __class__ { get; set; }
public string name { get; set; }
public string uid { get; set; }
}
public class Manufacturer
{
public string homepage_url { get; set; }
public string __class__ { get; set; }
public string name { get; set; }
public string uid { get; set; }
}
public class Item
{
public List<Offer> offers { get; set; }
public string uid { get; set; }
public string mpn { get; set; }
public List<object> redirected_uids { get; set; }
public Brand brand { get; set; }
public string octopart_url { get; set; }
public string __class__ { get; set; }
public Manufacturer manufacturer { get; set; }
}
public class Result
{
public List<Item> items { get; set; }
public int hits { get; set; }
public string __class__ { get; set; }
public object reference { get; set; }
public object error { get; set; }
}
public class RootObject
{
public int msec { get; set; }
public Request request { get; set; }
public string __class__ { get; set; }
public List<Result> results { get; set; }
}
}
The problem is at design-time, when I declare a variable with the type of my class:
ResponseType Response = new ResponseType();
Intellisense does not allow me to access the subclasses RootObject.results list. It only shows Equals, GetHashCode, GetType and ToString. I am assuming I did something wrong in my class declaration.
Thank you in advance!
Edit -- I am fairly new to C Sharp. I am trying to parse a response from a REST API. I took the JSON provided by the Rest API and converted it using json2csharp into a class. My intent was to do something like this
Within a function return:
public ResponseType ExecuteSearch(String PartNumber)
{
~ ALL CODE FOR GENERATING req
// Perform the search and obtain results
var data = client.Execute(req).Content;
JSON = data;
return JsonConvert.DeserializeObject<ResponseType>(data);
}
Then being able to access the response as an object outside of the function
Edit 2:
I figured out what I did. Instead of nesting everything within the ResponseType I should have simply renamed RootObject to ResponseType.
Intellisense does not allow me to access the subclasses RootObject.results list
it is because the property results is not static and you try to acces it this way. A static property is accessed via ClassName.PropertyName. For more information on static variables check the link.
It only shows Equals, GetHashCode, GetType and ToString
This is the basic set of methods that every object in C# inherits from the class object. This is why you can see it.
Intellisense will allow you to do this:
ResponseType.RootObject ro = new ResponseType.RootObject();
ro.results.First();
because you will need an Instance of that class to acces the property results.
I am assuming I did something wrong in my class declaration.
It depends. Basically if the compiler does not complain then you declared your classes as supposed to be. But the declaration of the properties commands you to access them in a specific way. So if you still want to access results with RootObject.results you need to make it static:
public class RootObject
{
public static List<Result> results { get; set; }
}
But note that this list will exist only once! and is not individual to each instance of RootObject! Since you have embedded classes you need to call it like this:
ResponseType.RootObject.results.WhatEver();
EDIT
I guess you would like to get the Object of type RootObject inside the Object of type ResponseType. If I am right then it is not necessary to declare the classes inside ResponseType but you have to declare variables of each type inside it like:
public class ResponseType
{
public RootObject MyRootObject{ get; set; }
}
public class RootObject
{
public int msec { get; set; }
public Request request { get; set; }
public string __class__ { get; set; }
public List<Result> results { get; set; }
}
Now you will be able to access the results variable inside the ResponseType object:
ResponseType rt = new ResponseType();
rt.MyRootObject.results.WhatEver();
For more information on how to deserialize JSON to classes please read the Deserialize JSON to C# Classes post
1) Object with ResponseType class isn't contain any fields(event static one).
2) You declare ResponseType object, but results is field of RootObject object.
So if you want to work with results you should do something like this:
ResponseType.RootObject rootObject = new ResponseType.RootObject();
rootObject.results.DoWork();
Below is what I think you are trying to do. I would only use it in this form if this is some kind of Data Transfer Object (DTO) because otherwise it is pretty bad practice for a class that would be used in code (mostly because of the public getters and setters on all of the fields and the field names matching the class name), but it does show your main mistake and that is that classes need to be defined outside of your main class and if you need that type of class in your top level class you need to define a public field to access it.
public class ResponseType
{
public Query Query { get; set; }
public Request Request { get; set; }
public Seller Seller { get; set; }
public Prices Prices { get; set; }
public Offer Offer { get; set; }
public Brand Brand { get; set; }
public Manufacturer Manufacturer { get; set; }
public Item Item { get; set; }
public Result Result { get; set; }
public RootObject RootObject { get; set; }
}
public class Query
{
public string q { get; set; }
public object sku { get; set; }
public int limit { get; set; }
public object reference { get; set; }
public object mpn_or_sku { get; set; }
public string mpn { get; set; }
public object brand { get; set; }
public string __class__ { get; set; }
public int start { get; set; }
public object seller { get; set; }
}
public class Request
{
public bool exact_only { get; set; }
public string __class__ { get; set; }
public List<Query> queries { get; set; }
}
public class Seller
{
public string display_flag { get; set; }
public bool has_ecommerce { get; set; }
public string name { get; set; }
public string __class__ { get; set; }
public string homepage_url { get; set; }
public string id { get; set; }
public string uid { get; set; }
}
public class Prices
{
public List<List<object>> USD { get; set; }
public List<List<object>> JPY { get; set; }
public List<List<object>> CNY { get; set; }
}
public class Offer
{
public string sku { get; set; }
public string packaging { get; set; }
public string on_order_eta { get; set; }
public string last_updated { get; set; }
public int? order_multiple { get; set; }
public int in_stock_quantity { get; set; }
public string eligible_region { get; set; }
public int? moq { get; set; }
public int? on_order_quantity { get; set; }
public object octopart_rfq_url { get; set; }
public string __class__ { get; set; }
public Seller seller { get; set; }
public string product_url { get; set; }
public object factory_order_multiple { get; set; }
public string _naive_id { get; set; }
public int? factory_lead_days { get; set; }
public Prices prices { get; set; }
public bool is_authorized { get; set; }
public bool is_realtime { get; set; }
}
public class Brand
{
public string homepage_url { get; set; }
public string __class__ { get; set; }
public string name { get; set; }
public string uid { get; set; }
}
public class Manufacturer
{
public string homepage_url { get; set; }
public string __class__ { get; set; }
public string name { get; set; }
public string uid { get; set; }
}
public class Item
{
public List<Offer> offers { get; set; }
public string uid { get; set; }
public string mpn { get; set; }
public List<object> redirected_uids { get; set; }
public Brand brand { get; set; }
public string octopart_url { get; set; }
public string __class__ { get; set; }
public Manufacturer manufacturer { get; set; }
}
public class Result
{
public List<Item> items { get; set; }
public int hits { get; set; }
public string __class__ { get; set; }
public object reference { get; set; }
public object error { get; set; }
}
public class RootObject
{
public int msec { get; set; }
public Request request { get; set; }
public string __class__ { get; set; }
public List<Result> results { get; set; }
}
Here's that I have in my WPF application:
public static class MappingCreator
{
public static void CreateMaps()
{
Mapper.CreateMap<SO.Services.Data.ServiceModel.Types.Customer, Customer>();
Mapper.CreateMap<List<SO.Services.Data.ServiceModel.Types.CustomerSearchResult>, List<CustomerSearchResult>>();
Mapper.AssertConfigurationIsValid();
}
}
CreateMaps() is called once on application start.
DTO:
namespace SO.Services.Data.ServiceModel.Types
{
[DataContract]
public class CustomerSearchResult
{
[DataMember]
public int CustomerId { get; set; }
[DataMember]
public string AccountType { get; set; }
[DataMember]
public string ShortName { get; set; }
[DataMember]
public string LegacyName { get; set; }
[DataMember]
public string LegacyContactName { get; set; }
[DataMember]
public string City { get; set; }
[DataMember]
public string StateAbbreviation { get; set; }
[DataMember]
public string Country { get; set; }
[DataMember]
public string PostalCode { get; set; }
}
}
Model:
namespace SO.Models
{
public class CustomerSearchResult : BindableBase
{
public int CustomerId { get; set; }
public string AccountType { get; set; }
public string ShortName { get; set; }
public string LegacyName { get; set; }
public string LegacyContactName { get; set; }
public string City { get; set; }
public string StateAbbreviation { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
}
}
Extension method:
public static class DtoMappingExtensions
{
public static List<CustomerSearchResult> ToModels(this List<SO.Services.Data.ServiceModel.Types.CustomerSearchResult> customerSearchList)
{
return Mapper.Map<List<SO.Services.Data.ServiceModel.Types.CustomerSearchResult>, List<CustomerSearchResult>>(customerSearchList);
}
}
I call a servicestack service which returns a List<SO.Services.Data.ServiceModel.Types.CustomerSearchResult> ... when I use the ToModels extension method against it, it returns a List with 0 records, even though the source list had 25k or so records.
I'm stumped.
In your CreateMaps() you would specify the object mappings, not the list mapping.
Mapper.CreateMap<SO.Services.Data.ServiceModel.Types.CustomerSearchResult, CustomerSearchResult>().ReverseMap();
And then in your ToModels() you do
Mapper.Map<List<CustomerSearchResult>, List<SO.Services.Data.ServiceModel.Types.CustomerSearchResult>>(customerSearchList);
I think the problem is here in this line.
Mapper.CreateMap<List<SO.Services.Data.ServiceModel.Types.CustomerSearchResult>, List<CustomerSearchResult>>();
this statement has no need because the AutoMapper will handle the Mapping of the list automatically when you Map the Classes.
I think you should replace the previous statement with this one
Mapper.CreateMap<SO.Services.Data.ServiceModel.Types.CustomerSearchResult, CustomerSearchResult>();
I have this specific JSON response that I am trying to deserialize without success. I am hoping someone can help me.
Here is the JSON response I get:
{
"num_locations": 1,
"locations": {
"98765": {
"street1": "123 Fake Street",
"street2": "",
"city": "Lawrence",
"state": "Kansas",
"postal_code": "66044",
"s_status": "20",
"system_state": "Off"
}
}
}
I used json2csharp http://json2csharp.com and got these recommended classes:
public class __invalid_type__98765
{
public string street1 { get; set; }
public string street2 { get; set; }
public string city { get; set; }
public string state { get; set; }
public string postal_code { get; set; }
public string s_status { get; set; }
public string system_state { get; set; }
}
public class Locations
{
public __invalid_type__98765 __invalid_name__98765 { get; set; }
}
public class RootObject
{
public int num_locations { get; set; }
public Locations locations { get; set; }
}
But when I try to use it in my code:
var locationResponse = JsonConvert.DeserializeObject<RootObject>(response.Content);
What I get is (Watch):
locationResponse : {RestSharpConsoleApplication.Program.RootObject} : RestSharpConsoleApplication.Program.RootObject
locations : {RestSharpConsoleApplication.Program.Locations} : RestSharpConsoleApplication.Program.Locations
__invalid_name__98765 : null : RestSharpConsoleApplication.Program.__invalid_type__98765
num_locations : 1 : int
Obviously I am not creating (json2csharp) the right classes for the DeserializeObject, and sadly I have no control over the JSON response (vendor = SimpliSafe).
It is obvious the "98765" is meant to be a value (location number) but json2csharp makes it into this __invalid_type__98765 class and this is probably why it gets null.
Any idea how should the classes look for this particular JSON to be successfully deserialized?
Thanks!
Zachs
You should be able to do this with a dictionary:
public class MyData{
[JsonProperty("locations")]
public Dictionary<string, Location> Locations {get;set;}
}
public class Location
{
public string street1 { get; set; }
public string street2 { get; set; }
public string city { get; set; }
public string state { get; set; }
public string postal_code { get; set; }
public string s_status { get; set; }
public string system_state { get; set; }
}
Do you have a non-Express version of Visual Studio? If so, copy the JSON to clipboard and then go to the Visual Studio menu: Edit >> Paste special >> Paste JSON as classes.
Using that gives:
public class Rootobject {
public int num_locations { get; set; }
public Locations locations { get; set; }
}
public class Locations {
public _98765 _98765 { get; set; }
}
public class _98765 {
public string street1 { get; set; }
public string street2 { get; set; }
public string city { get; set; }
public string state { get; set; }
public string postal_code { get; set; }
public string s_status { get; set; }
public string system_state { get; set; }
}
That suggests your JSON structure is not quite right.
You can also specify the property name via an attribute to use to get around this:
public class RootObject
{
public int num_locations { get; set; }
public Locations locations { get; set; }
}
public class Locations
{
[JsonProperty("98765")]
public LocationInner Inner { get; set; }
}
public class LocationInner
{
public string street1 { get; set; }
public string street2 { get; set; }
public string city { get; set; }
public string state { get; set; }
public string postal_code { get; set; }
public string s_status { get; set; }
public string system_state { get; set; }
}
...but it would really be better if the JSON were properly formatted such that the Locations was actually an array of location objects.