I am trying to use the fanart.tv webservice API but have a couple of issues.
I am using Json.Net (Newtonsoft.Json) and with other web-services I have de-serialized the JSON reponses into C# objects directly.
The issue here is that the element names are changing. Eg. if I search for artist thumbs for Metallica you get
{"Metallica":{"mbid_id":"65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab","artistthumb": [{"id":"36181","url":"http://assets.fanart.tv/fanart/music/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab/artistthumb/metallica-4fd83b0129f83.jpg","likes":"1"},...]}}
So the root element is called Metallica. Obviously search for another artist and you get a different element name. After attempting to deserialize this to an object I gave up and as in reality all I needed was a list of strings (the urls) I tried to process the JSON
var obj = _downloader.Download<JObject>(url);
if (obj != null)
{
if (obj.HasValues)
{
var fanartArtist = (JProperty)obj.First;
if (fanartArtist.HasValues)
{
var thumbs = fanartArtist.Value[SearchSubTypeToString(subType)];
if (thumbs.HasValues)
{
thumbUrls.AddRange(thumbs.Select(thumb => thumb["url"].ToString()));
}
}
}
}
which works fine when there is a response but if there are no thumbs the web-service returns null and this code fails with
Unable to cast object of type 'Newtonsoft.Json.Linq.JValue' to type 'Newtonsoft.Json.Linq.JObject'.
To complicate matters slightly I am sort of limited by the application and ideally I need to use
JsonConvert.DeserializeObject<TE>(json);
So the question is what what is the best approach to solve both of these issues?
Try this:
First, define objects to hold the data deserialized from the JSON:
class Artist
{
public Guid mb_id { get; set; }
public List<Thumb> artistthumb { get; set; }
}
class Thumb
{
public int id { get; set; }
public string url { get; set; }
public int likes { get; set; }
}
You can then deserialize it like this (where json is a string containing the JSON data from the web service):
Dictionary<string, Artist> artists =
JsonConvert.DeserializeObject<Dictionary<string, Artist>>(json);
Once deserialized, you can access the data like this:
foreach (KeyValuePair<string, Artist> kvp in artists)
{
Console.WriteLine("Urls for " + kvp.Key + ":");
foreach (Thumb thumb in kvp.Value.artistthumb)
{
Console.WriteLine(thumb.url);
}
}
Assuming the data you showed in your question, the output would look like this:
Urls for Metallica:
http://assets.fanart.tv/fanart/music/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab909e7ae6b2ab/artistthumb/metallica-4fd83b0129f83.jpg
.
Related
I'm using a HTTP client to get a string and picking out my json from that and converting back to a string to deserialize it into a List of "Spots" but can't get it to to work
I've tried changing the DeserializeObject type to every mix of "List, IList, HardwareUpdateSpot, HardWareModel" and still it didn't work
public async Task<IList<HardwareUpdateSpot>> UpdateSpotHTTP()
{
var client = new HttpClient();
var response = await client.GetAsync(
"https://io.adafruit.com/api/v2/Corey673/feeds/673d855c-9f66-4e49-8b2c-737e829d880c");
var responseHTTP = response.Content.ReadAsStringAsync();
var j = JObject.Parse(responseHTTP.Result);
var b = j.GetValue("last_value");
var h = b.ToString();
var dataObjects = JsonConvert.DeserializeObject<IList<HardwareUpdateSpot>>(h);
return null;
}
public record HardWareModel
{
public int SpotId { get; set; }
public string Occupied { get; set; }
}
public class HardwareUpdateSpot
{
public IList<HardWareModel> Spots { get; set; }
public HardwareUpdateSpot(IList<HardWareModel> spots)
{
Spots = spots;
}
}
While trying to reproduce your problem I have examined the returned value from the API call. This is the json returned:
{"Spot":[
{"SpotId":"1","Occupied":"false",},
{"SpotId":"2","Occupied":"false",},
{"SpotId":"3","Occupied":"false",},
{"SpotId":"4","Occupied":"false"}
]}
So, it easy to see that the returned json requires a root object with a public Spot property (not Spots) and this property should be a collection.
Instead the code above expects a json that has at the root level a collection of HardwareUpdateSpot and of course it cannot work.
To fix the problem you need to change the deserialization to:
JsonConvert.DeserializeObject<HardwareUpdateSpot>(h);
Now, you need to make some changes to the HardwareUpdateSpot class to make it compatible with the json.
First you need to add a parameterless constructor required by jsonconvert, then you need to fix the difference between the name for the property (Spots) and the name returned (Spot).
So you can change the property name to match the json or add the attribute that make Spots=Spot
[JsonProperty("Spot")]
public IList<HardWareModel> Spots { get; set; }
I asked a question a couple of days ago to collect data from MongoDB as a tree.
MongoDB create an array within an array
I am a newbie to MongoDB, but have used JSON quite substantially. I thought using a MongoDB to store my JSON would be a great benefit, but I am just experiencing immense frustration.
I am using .NET 4.5.2
I have tried a number of ways to return the output from my aggregate query to my page.
public JsonResult GetFolders()
{
IMongoCollection<BsonDocument> collection = database.GetCollection<BsonDocument>("DataStore");
PipelineDefinition<BsonDocument, BsonDocument> treeDocs = new BsonDocument[]
{
// my query, which is a series of new BsonDocument
}
var documentGroup = collection.Aggregate(treeDocs).ToList();
// Here, I have tried to add it to a JsonResult Data,
// as both documentGroup alone and documentGroup.ToJson()
// Also, loop through and add it to a List and return as a JsonResult
// Also, attempted to serialise, and even change the JsonWriterSettings.
}
When I look in the Immediate Window at documentGroup, it looks exactly like Json, but when I send to browser, it is an escaped string, with \" surrounding all my keys and values.
I have attempted to create a model...
public class FolderTree
{
public string id { get; set; }
public string text { get; set; }
public List<FolderTree> children { get; set; }
}
then loop through the documentGroup
foreach(var docItem in documentGroup)
{
myDocs.Add(BsonSerializer.Deserialize<FolderTree>(docItem));
}
but Bson complains that it cannot convert int to string. (I have to have text and id as a string, as some of the items are strings)
How do I get my MongoDB data output as Json, and delivered to my browser as Json?
Thanks for your assistance.
========= EDIT ===========
I have attempted to follow this answer as suggested by Yong Shun below, https://stackoverflow.com/a/43220477/4541217 but this failed.
I had issues, that the "id" was not all the way through the tree, so I changed the folder tree to be...
public class FolderTree
{
//[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
//public string id { get; set; }
[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
public string text { get; set; }
public List<FolderTreeChildren> children { get; set; }
}
public class FolderTreeChildren
{
[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
public string text { get; set; }
public List<FolderTreeChildren> children { get; set; }
}
Now, when I look at documentGroup, I see...
[0]: {Plugins.Models.FolderTree}
[1]: {Plugins.Models.FolderTree}
To be fair to sbc in the comments, I have made so many changes to get this to work, that I can't remember the code I had that generated it.
Because I could not send direct, my json result was handled as...
JsonResult json = new JsonResult();
json.Data = documentGroup;
//json.Data = JsonConvert.SerializeObject(documentGroup);
json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return json;
Note, that I also tried to send it as...
json.Data = documentGroup.ToJson();
json.Data = documentGroup.ToList();
json.Data = documentGroup.ToString();
all with varying failures.
If I leave as documentGroup, I get {Current: null, WasFirstBatchEmpty: false, PostBatchResumeToken: null}
If I do .ToJson(), I get "{ \"_t\" : \"AsyncCursor`1\" }"
If I do .ToList(), I get what looks like Json in json.Data, but get an error of Unable to cast object of type 'MongoDB.Bson.BsonInt32' to type 'MongoDB.Bson.BsonBoolean'.
If I do .ToString(), I get "MongoDB.Driver.Core.Operations.AsyncCursor`1[MongoDB.Bson.BsonDocument]"
=========== EDIT 2 =================
As this way of extracting the data from MongoDB doesn't want to work, how else can I make it work?
I am using C# MVC4. (.NET 4.5.2)
I need to deliver json to the browser, hence why I am using a JsonResult return type.
I need to use an aggregate to collect from MongoDB in the format I need it.
My Newtonsoft.Json version is 11.0.2
My MongoDB.Driver is version 2.11.1
My method is the simplest it can be.
What am I missing?
Let me preface by saying I'm very new to C# development so if the solution seems obvious I apologize.
I'm getting a JSON string back from a user and I need to filter a list of C# objects based on what the JSON string contains. The JSON can only have fields that my C# model has but I don't know what fields the JSON string will contain. My C# model looks something like this:
public class Enrollment {
public int Year { get; set; }
public int NumEnrolls { get; set; }
public int DaysIntoEnrollment { get; set; }
public string Name { get; set; }
}
The JSON will have one or more of these properties with values to filter out. It could look like this:
{
"Year": ["2020", "2019"],
"Name": ["CourseA", "CourseB"],
"DaysIntoEnrollment": "20"
}
I need to filter my list of Enrollment objects based on the above JSON. So I would want the end result to have all Enrollment objects that don't contain a Year of 2020 or 2019 for example.
I've gotten a filter to work with linq on a single property but my real model has much more properties that can be filtered and I'm looking for a compact solution that will work regardless of which properties are included in the JSON. This is what I have working
public void GetFilteredData(string filters) {
var enrollList = new List<Enrollments>(); // Pretend this contains a list of valid Enrollment data
var json = JObject.Parse(filters); // filters string is in the json format from above
var propsToFilter =
from p in json["Year"]
select p;
var filtered = enrollList.Where(e => !propsToFilter.Contains(e.Year.ToString())));
}
Is there a simple way to do this without manually going through each property like I did above?
I have at times made it my life's work to avoid API's at all costs (that's a debate for another day) but the time is arriving for this to change, a few months ago I got started on creating an API for my applications, and thanks to this very website, it's worked a charm.
So now I'm creating a simple Windows Console Application, it should do nothing more than get API Data then submit to a database for the primary application to use at a later date.
So far so good or so I thought.
This is what I came up with:
static void Main(string[] args)
{
string pair = "xxx/xxx";
string apiUrl = "http://someURL" + pair;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader readerStream = new StreamReader(responseStream, System.Text.Encoding.GetEncoding("utf-8"));
string json = readerStream.ReadToEnd();
readerStream.Close();
var jo = JObject.Parse(json);
Console.WriteLine("Pair : " + (string)jo["pair"]);
Console.WriteLine("Open : " + (string)jo["openPrice"]);
Console.WriteLine("Close : " + (string)jo["closePrice"]);
Console.WriteLine("Vol : " + (string)jo["vol"]);
Console.ReadKey();
}
Now this works FANTASTIC for the primary source, but when I change the source (eventually it will be multi source) it fails to work.
After some investigation, it seems the slightly different responses are the culprit.
API Return looks like this
{
"ip":"1.1.1.1","country_code":"AU","country_name":"Australia",
"region_code":"VIC","region_name":"Victoria","city":"Research",
"zip_code":"3095","time_zone":"Australia/Melbourne","latitude":-37.7,
"longitude":145.1833,"metro_code":0
}
The return for an alternative source looks like this
{
"success":true,"message":"",
"result":[
{"MarketName":"BITCNY-BTC","High":8000.00000001,"Low":7000.00000000,
"Volume":0.02672075, "Last":7000.00000000,"BaseVolume":213.34995000,
"TimeStamp":"2017-02-09T08:38:22.62","Bid":7000.00000001,"Ask":9999.99999999,
"OpenBuyOrders":14,
"OpenSellOrders":20,"PrevDay":8000.00000001,"Created":"2015-12-11T06:31:40.653"
}
]
}
As we can see the second return is structured differently, and for this feed I've been unable to work this out, clearly my code works, kind of, and clearly it doesn't.
I've looked about on the net and am still no closer to a solution, partly I don't know what I'm really asking and secondly google only wants to talk webAPI.
If there is someone who can point me in the right direction, I don't want the work done for me per say that solves nothing, I have got to learn to do this one way or another.
As mentioned in the comments, your strings are unrelated. You won't be able to perform the same actions on both JSON strings because they're different.
Your best bet is to work with Deserialization to objects, which is converting a JSON string into an object and working with properties instead of JObject literals.
Putting your first JSON string into Json2Csharp.com, this class structure was produced:
public class RootObject
{
public string ip { get; set; }
public string country_code { get; set; }
public string country_name { get; set; }
public string region_code { get; set; }
public string region_name { get; set; }
public string city { get; set; }
public string zip_code { get; set; }
public string time_zone { get; set; }
public double latitude { get; set; }
public double longitude { get; set; }
public int metro_code { get; set; }
}
You could then make some adjustments to your code to extract the HTTP request functionality, assuming that you'll only be working with JSON strings in this instance, and then simply deserialized the JSON to an object.
An example of a WebRequest() method would be:
private static string WebRequest(string UrlToQuery)
{
using(WebClient client = new WebClient)
{
return client.DownloadString(UrlToQuery);
}
}
(Make sure you catch any errors and deal with unexpected responses, I'll leave that up to you!)
You can then call this method like so:
string JsonResponse = WebRequest(apiUrl);
and deserialize to an object of type RootObject like so:
RootObject deserializedString = JsonConvert.Deserialize<RootObject>(JsonResponse);
You'll then be able to perform something like:
Console.WriteLine($"Country name: {deserializedString.country_name}");
Which will print the value of the country_name property of your deserializedString object.
I'm trying to parse a json file with Json.net. The content of the json file is:
[
[ "240521000", "37.46272", "25.32613", "0", "71", "90", "15", "2016-07-18T21:09:00" ],
[ "237485000", "37.50118", "25.23968", "177", "211", "273", "8", "2015-09-18T21:08:00" ]
]
I created the following code:
WebClient wc = new WebClient();
string json = wc.DownloadString("data.json");
dynamic myObject = JsonConvert.DeserializeObject<dynamic>(json);
foreach (string item in myObject[0])
{
var x = item[0];
}
How can I loop through all the individual items without having a key?
You probably just need two nested foreach statements. Try something like this:
foreach (var items in myObject)
{
foreach (var item in items)
{
// do something
}
}
While diiN_'s answer answers the question, I don't think it's a good solution. Having had a look at the Marine Traffic API, it feels like they've made a poor JSON implementation, as the XML representation clearly has attribute names for the values. Their JSON should've been:
{"positions": ["position": {"mmsi": "311029000", "lat": "37.48617", "long": "24.37233", ...}]}
Because it isn't, we have a JSON string instead where it's a nested array, where the array is a two-dimensional array, and you'd have to hope the data model isn't changed to remove something, as you'd have to use an index to retrieve data.
However, if you look at the XML available from the API, the attributes have names. I would suggest that you instead download the XML, and parse this into an object - a model, in ASP.NET - which is strongly typed, and can be used more easily in a View.
Here's an example that I've got running. It uses XML parsing to first read the XML from the API, and then parse it to JSON, and finally an actual object.
First, the model class (Position.cs)
public sealed class Position
{
[JsonProperty("#MMSI")]
public string MMSI { get; set; }
[JsonProperty("#LAT")]
public string Latitude { get; set; }
[JsonProperty("#LON")]
public string Longitude { get; set; }
[JsonProperty("#SPEED")]
public string Speed { get; set; }
[JsonProperty("#HEADING")]
public string Heading { get; set; }
[JsonProperty("#COURSE")]
public string Course { get; set; }
[JsonProperty("#STATUS")]
public string Status { get; set; }
[JsonProperty("#TIMESTAMP")]
public string TimeStamp { get; set; }
}
Next, the parsing logic:
var client = new WebClient();
var xml = client.DownloadString("data.xml");
var doc = new XmlDocument();
doc.LoadXml(xml);
var json = JsonConvert.SerializeXmlNode(doc);
var positions = JObject.Parse(json).SelectToken("pos").SelectToken("row").ToObject<List<Position>>();
At the end of the parsing logic, you now have a list of Positions which you can pass to your view, and have it be strongly typed.
As a brief example:
// after you have the positions list
return View(positions);
Positions.cshtml
#model List<Positions>
<h2>Positions</h2>
#foreach (var position in Model)
{
<p>#position.MMSI (#position.Latitude, #position.Longitude)</p>
}
I hope this is useful to you. If you have any questions, drop me a comment.