I've got a problem regarding Json.NET and the omdbapi. I'm trying to retrieve information from the omdbapi and some properties are giving me headaches, particularly the "imdbVotes" one since it's written, in example, as "321,364" so I can't get an integer from it.
I'm betting that I need a custom converter, but I'm afraid that, at the moment, I don't really understand how to create one for my particular problem.
All other properties work well (I'm not using all of them at the moment).
This is the response for, lets say Snatch : http://www.omdbapi.com/?i=&t=snatch
This is my class :
public class MovieJSON
{
[JsonProperty(PropertyName = "Title")]
public String Title { get; set; }
[JsonProperty(PropertyName = "Year")]
public int Year { get; set; }
[JsonProperty(PropertyName = "Genre")]
public String Genre { get; set; }
[JsonProperty(PropertyName = "Director")]
public String Director { get; set; }
[JsonProperty(PropertyName = "Actors")]
public String Actors { get; set; }
[JsonProperty(PropertyName = "Plot")]
public String Plot { get; set; }
[JsonProperty(PropertyName = "Poster")]
public String Poster { get; set; }
[JsonProperty(PropertyName = "Metascore")]
public int Metascore { get; set; }
[JsonProperty(PropertyName = "imdbRating")]
public decimal ImdbRating { get; set; }
[JsonProperty(PropertyName = "imdbVotes")]
public int ImdbVotes { get; set; }
}
UPDATE #1 :
How can I handle the response when the property has the value "N/A"?. That happens for some movies (ie. http://www.omdbapi.com/?i=&t=four+rooms has it's Metascore set to N/A).
UPDATE #2 :
Another related inquiry. I'm using EF6 with MySQL and the idea's to populate the database with movies created through JSON parsing.
This is my Movie class :
[JsonObject(MemberSerialization.OptIn)]
[Table("movies")]
public class MovieJSON
{
[Key]
public int Id { get; set; }
[JsonProperty(PropertyName = "Title")]
[Column("title")]
public String Title { get; set; }
[JsonProperty(PropertyName = "Year")]
[Column("year")]
public int Year { get; set; }
[JsonProperty(PropertyName = "Genre")]
[Column("genre")]
public String Genre { get; set; }
[JsonProperty(PropertyName = "Director")]
[Column("director")]
public String Director { get; set; }
[JsonProperty(PropertyName = "Actors")]
[Column("actors")]
public String Actors { get; set; }
[JsonProperty(PropertyName = "Plot")]
[Column("plot")]
public String Plot { get; set; }
[JsonProperty(PropertyName = "Poster")]
[Column("poster")]
public String Poster { get; set; }
[JsonProperty(PropertyName = "Metascore")]
public String Metascore { get; set; }
[Column("metascore")]
public int MetascoreInt
{
get
{
int result;
if (int.TryParse(Metascore, NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out result))
return result;
return 0;
}
}
[JsonProperty(PropertyName = "imdbRating")]
public String ImdbRating { get; set; }
[Column("imdb_rating")]
public Decimal ImdbRatingDecimal
{
get
{
Decimal result;
if (Decimal.TryParse(ImdbRating, out result))
return result;
return 0;
}
}
[JsonProperty(PropertyName = "imdbVotes")]
public String ImdbVotes { get; set; }
[Column("imdb_votes")]
public long ImdbVotesLong
{
get
{
long result;
String stringToParse = ImdbVotes.Remove(ImdbVotes.IndexOf(','), 1);
if (long.TryParse(stringToParse, out result))
return result;
return 0;
}
}
[JsonProperty(PropertyName = "imdbID")]
[Column("imdb_id")]
public String ImdbID { get; set; }
[JsonProperty(PropertyName = "type")]
[Column("type")]
public String Type { get; set; }
public override string ToString()
{
String[] propertiesToIgnore = {"MetascoreInt", "ImdbRatingDecimal", "ImdbVotesLong"};
var sb = new StringBuilder();
PropertyInfo[] properties = GetType().GetProperties();
foreach (PropertyInfo propertyInfo in properties)
{
if (propertiesToIgnore.Contains(propertyInfo.Name))
continue;
sb.AppendLine(String.Format("{0} : {1} ",
propertyInfo.Name, propertyInfo.GetValue(this, null)));
}
return sb.ToString();
}
}
This is my EF6 configuration-context class (I'm ignoring the String fields and instead, using the Helper ones since the database is configured to accept int for Metascore and so on) :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MovieJSON>().Ignore(e => e.Metascore).Ignore(e => e.ImdbRating).Ignore(e => e.ImdbVotes);
base.OnModelCreating(modelBuilder);
}
Additional image info :
Object values before insertion into the database (all values are properly set)
Valid XHTML http://imagizer.imageshack.us/v2/800x600q90/689/8x5m.png
Values in the database :
Valid XHTML http://imagizer.imageshack.us/v2/800x600q90/844/nvc5.png
The helper fields (MetascoreInt, ImdbRatingDecimal, ImdbVotesLong) are returning zero, I can't figure out why.
Any help would be mucho appreciated! :)
All the best
You could have two properties: one would be the string property as it comes from IMDB, and the other would be the int property that converts the string one. To convert, you can use the nifty NumberStyles.AllowThousands flag. So you would have
[JsonProperty(PropertyName = "imdbVotes")]
public string ImdbVotes { get; set; }
public int ImdbVotesInt
{
get
{
int result;
if (int.TryParse(ImdbVotes,
NumberStyles.AllowThousands,
CultureInfo.InvariantCulture,
out result))
return result; // parse is successful, use 'result'
else
return 0; // parse is unsuccessful, return whatever default value works for you
}
}
Related
I am working on a Xamarin.Forms Project and I am at a dead-end of sorts. My issue is that I want to display user transactions which I pull from a server, in a listview, however I need four different pull requests to get all the data which means I have four different objects lists which I grouped by the transaction number as you can see in this screenshot:
The key transaction number can be seen and if you expand you'll see the other data within each transaction
Here is the code where I group the deserialised json lists with the common key:
var t = JsonConvert.DeserializeObject<List<trans_mod>>(transactions);
var l = JsonConvert.DeserializeObject<List<loc_mod>>(loc);
var d = JsonConvert.DeserializeObject<List<disc_mod>>(disc);
var it = JsonConvert.DeserializeObject<List<item_mod>>(itm);
var q = it.AsQueryable().GroupBy(g => g.trans).ToList();
var q2= d.AsQueryable().GroupBy(g => g.trans).ToList();
var q3 = l.AsQueryable().GroupBy(g => g.trans).ToList();
var q4 = t.AsQueryable().GroupBy(g => g.position).ToList();
Object Models for each list
public class loc_mod
{
[DataMember]
public string location { get; set; }
[JsonProperty(PropertyName = "#modify_stamp")]
public string stamp { get; set; }
[JsonProperty(PropertyName = "$trans")]
public string trans { get; set; }
}
public class disc_mod
{
[DataMember]
public string discount { get; set; }
[JsonProperty(PropertyName = "#modify_stamp")]
public string stamp { get; set; }
[JsonProperty(PropertyName = "$trans")]
public string trans { get; set; }
}
public class item_mod
{
[JsonProperty(PropertyName = "item.price")]
public string price { get; set; }
[JsonProperty(PropertyName = "item.name")]
public string name { get; set; }
[JsonProperty(PropertyName = "#modify_stamp")]
public string stamp { get; set; }
[JsonProperty(PropertyName = "$trans")]
public string trans { get; set; }
}
public class trans_mod
{
[DataMember]
public string refer { get; set; }
[DataMember]
public string date { get; set; }
[DataMember]
public string time { get; set; }
[DataMember]
public int points { get; set; }
[DataMember]
public string _total { get; set; }
[JsonProperty(PropertyName = "$$position")]
public string position { get; set; }
[JsonProperty(PropertyName = "#modify_stamp")]
public string stamp { get; set; }
[JsonProperty(PropertyName = "$trans")]
public string trans { get; set; }
}
public class itms
{
public string price { get; set; }
public string name { get; set; }
public DateTime stamp { get; set; }
[JsonProperty(PropertyName = "$trans")]
public string trans { get; set; }
}
What I want to do is to loop through all four lists and add the data from each list in the listview but I can't think of a way I can do that.
Listview Add() code Example:
Transactions.Add(new Transaction
{
Details = "Date: " + ti[i].date + " | Time: " + ti[i].time + " |
Reference: " + ti[i].refer,
Isvisible = false, Items= ti[i].item, Total = ti[i].total, Discount
= ti[i].discount
});
Sorry if this is a bit confusing, it's confusing for me as well as I am a relative beginner. Any help is welcome!
Define an Interface that your item classes all implement.
That interface has a method that returns whatever you need for listview.
public Interface IHasTransaction
{
Transaction GetTransaction();
}
public class loc_mod : IHasTransaction
{
...
public Transaction GetTransaction()
{
// Use fields of this class to create a Transaction.
return new Transaction(...);
}
}
public class disc_mod : IHasTransaction
{
...
}
If you want, you can make a list that has a mixture of these:
public List<IHasTransaction> models = new List<IHasTransaction>();
models.Add(new loc_mod(...));
models.Add(new disc_mod(...));
Given any of these items
IHasTransaction model
You can easily get the corresponding Transaction:
model.GetTransaction()
OR
var lm = new loc_mod(...);
lm.GetTransaction()
I have a class like this
public class ListOfBMTTeamMapping
{
public class TeamMapping
{
public List<TeamMappings> results { get; set; }
}
public class TeamMappings
{
public int id { get; set; }
public string areaPath { get; set; }
public string agileReleaseTrainName { get; set; }
public string deliveryTeamName { get; set; }
public string keyedInTeamCode { get; set; }
public string deliveryTeamId { get; set; }
public bool isDeleted { get; set; }
public string modified { get; set; }
public string modifiedBy { get; set; }
}
}
And here is my model class to which I need the above API class to get copied
public class JsonBmtAdoMapping
{
public int? Id { get; set; }
public string AreaPath { get; set; }
public string AgileReleaseTrainName { get; set; }
public string DeliveryTeamName { get; set; }
public string KeyedInTeamCode { get; set; }
public string DeliveryTeamId { get; set; }
public string IsDeleted { get; set; }
public DateTime? Modified { get; set; }
public string ModifiedBy { get; set; }
}
So here is my code I tried
var format = "dd/MM/yyyy";
var dateTimeConverter = new IsoDateTimeConverter { DateTimeFormat = format };
ListOfBMTTeamMapping.TeamMapping Results = new ListOfBMTTeamMapping.TeamMapping();
Results = JsonConvert.DeserializeObject<ListOfBMTTeamMapping.TeamMapping>(responseBody);
List<JsonBmtAdoMapping> jM = new List<JsonBmtAdoMapping>();
jM = _mapper.Map<ListOfBMTTeamMapping.TeamMapping,List<JsonBmtAdoMapping>>(Results);
int n = 10;
And here is my automapper profile
CreateMap<ListOfBMTTeamMapping.TeamMapping, List<JsonBmtAdoMapping>>();
CreateMap<ListOfBMTTeamMapping.TeamMappings, JsonBmtAdoMapping>();
But when the code executes, Ofcourse I am getting the data in results variable without any trouble
But when the mapper code fires, it execute the line without any error, but no data being copied from source to my model class which is the destination
jM.count is always 0 when Results hold 124 rows of data
What I did wrong
Your mapping from TeamMapping to List<JsonBmtAdoMapping> can't be done out of the box by AutoMapper, because your source is an object with a property that contains the list and the destination is a list on itself.
So you have to tell him, how this conversion from a single object to a list can be done. Due to the fact, that you already have a mapping for each individual item, we can use that recursively within our mapping method.
By using this mapping, it should work:
CreateMap<ListOfBMTTeamMapping.TeamMappings, JsonBmtAdoMapping>();
CreateMap<ListOfBMTTeamMapping.TeamMapping, List<JsonBmtAdoMapping>>()
.ConvertUsing((src, _, context) => src.results.Select(context.Mapper.Map<JsonBmtAdoMapping>).ToList());
Update
Cause a mapper is already defined for the individual items and lists are handled automatically by AutoMapper we can even make it shorter (thanks for Lucian for the hint in the comments):
CreateMap<ListOfBMTTeamMapping.TeamMappings, JsonBmtAdoMapping>();
CreateMap<ListOfBMTTeamMapping.TeamMapping, List<JsonBmtAdoMapping>>()
.ConvertUsing((src, _, context) => context.Mapper.Map<List<JsonBmtAdoMapping>>(src.results));
Im using C# to get a file from my local pc data folder.
This is the code to do that:
var _rootpath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + directory;
var ENC = new Encryption();
var s = File.ReadAllText(_rootpath + "json");
var x = ENC.RijndaelDecrypt(s, App.EncryptionPassword);
This works fine so far.
x got now this value (so this is the string I want to convert to an object) :
{
"items":[
{
"id":194,
"guid":"594394",
"name":"Test",
"connectorId":248,
"customerId":1,
"customerName":"company",
"connectorTypeId":10,
"connectorTypeIcon":null,
"connectorCategoryId":1,
"vendor":"FasterForward",
"isActive":true,
"shopId":null,
"sku":null,
"workerBearerToken":"",
"workerUri":"http://localhost:9000"
}
],
"responseStatus":null
}
After this I want to convert this to an object
var _response = JsonConvert.DeserializeObject<CrmJobListResponse>(x);
This line gives an error:
{"Error converting value x to type 'ServiceModel.CrmJobListResponse'. Path '', line 1, position 991."}
ServiceModel.CrmJobListResponse:
namespace ServiceModel
{
public class CrmJobListResponse : ResponseBase
{
public CrmJobListResponse();
public List<CrmJob> Items { get; set; }
}
}
CrmJob class:
namespace ServiceModel.DTO
{
public class CrmJob : IHasId<int>
{
public CrmJob();
[Ignore]
public string WorkerBearerToken { get; set; }
[PropertyValue("sku")]
public string SKU { get; set; }
[PropertyValue("shop_id")]
public string ShopId { get; set; }
public bool IsActive { get; set; }
public string Vendor { get; set; }
public int ConnectorCategoryId { get; set; }
[Ignore]
public string WorkerRefreshToken { get; set; }
public string ConnectorTypeIcon { get; set; }
public string CustomerName { get; set; }
public int CustomerId { get; set; }
public int ConnectorId { get; set; }
[PropertyValue("jobname")]
public string Name { get; set; }
public string Guid { get; set; }
public int Id { get; set; }
public int ConnectorTypeId { get; set; }
[Ignore]
public string WorkerUri { get; set; }
}
}
Does anyone know why it can't convert my Json string to an object?
I didn't made the code myself, but I don't see why It should go wrong...
If you have a hard time creating DTOs you have some tools that may assist you https://app.quicktype.io/
You can also use paste special in VS to paste a Json directy to a C# class.
This also shows you if you malformed a Json.
I am currently setting some strings via this method:
string marketlabel = allmarketdata.#return.markets.COLXPM.label.ToString();
I would like to set the market label dynamically by having a string for the actual market choice.
string currentMarketSelected= this.marketTextBox.Text; // Specific market: COLXPM
string marketlabel=allmarketdata.#return.markets.currentMarketSelected.label.ToString();
I have been searching for a few hours and probably am not explaining correctly. I tried some stuff with reflections with no success. Basically what I want to do is have a textbox or list which contains all the market names and based on which one is selected start setting the data.
Above is the best type of example of what I want to do even though it is not syntactically possible to use a variable in place.
public class Markets
{
public COLXPM COLXPM { get; set; }
//Lots of markets below here
}
public class COLXPM
{
public string marketid { get; set; }
public string label { get; set; }
public string lasttradeprice { get; set; }
public string volume { get; set; }
public string lasttradetime { get; set; }
public string primaryname { get; set; }
public string primarycode { get; set; }
public string secondaryname { get; set; }
public string secondarycode { get; set; }
public List<Recenttrade> recenttrades { get; set; }
public List<Sellorder> sellorders { get; set; }
public List<Buyorder> buyorders { get; set; }
}
public class Return
{
public Markets markets { get; set; }
}
public class RootObject
{
public int success { get; set; }
public Return #return { get; set; }
}
The proposed solution below that worked
string currentMarketSelected = "DOGEBTC"; // Just selecting one of the markets to test it works
var property = allmarketdata.#return.markets.GetType().GetProperty(currentMarketSelected);
dynamic market = property.GetMethod.Invoke(allmarketdata.#return.markets, null);
string marketlabel = market.label.ToString(); //Gets all my selected market data
Here is a solution using reflection.
string currentMarketSelected= this.marketTextBox.Text; // Specific market: COLXPM
var property = allmarketdata.#return.markets.GetType().GetProperty(currentMarketSelected);
dynamic market = property.GetGetMethod().Invoke(allmarketdata.#return.markets, null);
string marketlabel=market.label.ToString();
You need something like this:
public class Markets
{
public COLXPM this[string key]
{
get
{
COLXPM colxpm;
switch (key)
{
// TODO : use "key" to select instance of COLXPM;
case "example1":
colxpm = ...;
break;
default:
throw new NotSupportedException();
}
return colxpm;
}
}
}
Then you can do something like:
string marketlabel=allmarketdata.#return.markets[currentMarketSelected]label.ToString();
This is an indexed property.
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);