JSON has rapidly grown arguably the most popular way to transfer data via API’s. It’s fast, simple, and most every language supports it. ASP.net has a few things like the JavaScript Deserializer Class that do a decent job, but one of the better tools available is JSON.net.
What a better way to test it than to use a real world example. StackOverflow has a public facing JSON api available at http://api.stackoverflow.com/1.1/ so I’ll show how to consume that one.
First, fire up whatever sort of app you want and then head to the Tools –> Library Package Manager –> Package Manager Console (aka. NuGet) and type in Install-Package Newtonsoft.Json
After some NuGet-y magic you’ll have JSON.net at your finger tips.
For starters we’ll consume the Users method of the API. Hitting the Url, http://api.stackoverflow.com/1.1/users/558672 It looks like…
The fastest way to consume it with JSON.net is by using the JsonConvert.DeserializeAnonymousType method.
var client = new WebClient(); byte[] response = client.DownloadData("http://api.stackoverflow.com/1.1/users/558672"); var decompress = new GZipStream(new MemoryStream(response), CompressionMode.Decompress); var reader = new StreamReader(decompress); string ret = reader.ReadToEnd(); var stackResponse = new { total = 0, page = 0, pagesize = 0, users = new[] { new { user_id = 0, user_type = "", creation_date = "", display_name = "", reputation = 0, email_hash = "", age = 0, last_access_date = 0, website_url = "", location = "", about_me = "", question_count = 0, answer_count = 0, view_count = 0, up_vote_count = 0, accept_rate = 0, bagde_counts = new { gold = 0, silver = 2, bronze = 9 } } } }; var stackUser = JsonConvert.DeserializeAnonymousType(ret, stackResponse);
One thing to note is that StackOverflow Gzips all of their API responses, that’s what all that decompress stuff is about. So, THAT is it as far as the quickest way to consume a JSON Api with JSON.net.
The next more involved way to consume the API is to create your own classes that mirror the structure of the API and deserialize the response into them. This can look a variety of ways potentially, but here is my implementation.
public class StackUser : StackBase { [JsonProperty("user_id")] public int UserId { get; set; } [JsonProperty("user_type")] public string UserType { get; set; } [JsonProperty("display_name")] public string DisplayName { get; set; } [JsonProperty("reputation")] public int Reputation { get; set; } [JsonProperty("email_hash")] public string Email { get; set; } [JsonProperty("age")] public int Age { get; set; } [JsonProperty("last_access_date")] public int LastAccessDateInt { get; set; } [JsonProperty("website_url")] public string Website { get; set; } [JsonProperty("location")] public string Location { get; set; } [JsonProperty("about_me")] public string AboutMe { get; set; } [JsonProperty("question_count")] public int QuesionCount { get; set; } [JsonProperty("answer_count")] public int AnswerCount { get; set; } [JsonProperty("view_count")] public int ViewCount { get; set; } [JsonProperty("up_vote_count")] public int UpVoteCount { get; set; } [JsonProperty("down_vote_count")] public int DownVoteCount { get; set; } [JsonProperty("accept_rate")] public int AccpetRate { get; set; } [JsonProperty("association_id")] public Guid AssociationId { get; set; } [JsonProperty("badge_counts")] public Dictionary Badges { get; set; } }
This class represents all of the properties for a User. The JsonProperty attribute specifies what the actual property looks like in the JSON response.
public interface IUserRepository { StackUser GetUserById(params string[] ids); } public class Users : StackRepository<UserResponse>, IUserRepository { public Users() { Method = "users"; } public StackUser GetUserById(params string[] ids) { var userResponse = Get(ids); return userResponse.Users.FirstOrDefault(); } } public class StackResponse : IStackResponse { public int Total { get; set; } public int Page { get; set; } public int PageSize { get; set; } } public class UserResponse : StackResponse { public List Users { get; set; } }
Here I created a UsersRepository to consume all of the methods available for users and declared UsersResponse and a base StackResponse for the paging information that comes back on every request. Then within the UserResponse I get back the StackUser object off of UserResponse.Users
public abstract class StackRepository : IStackRepository { public string Method { get; set; } public RequestOptions Options { get; set; } private const string ApiUrl = "http://api.stackoverflow.com/1.1/"; public string Request(string request, RequestOptions options = null) { var client = new WebClient(); var stackRequest = new StringBuilder(ApiUrl + request); byte[] response = client.DownloadData(stackRequest.ToString()); if(response == null) { throw new Exception(string.Format("No response from request {0}", request)); } var decompress = new GZipStream(new MemoryStream(response), CompressionMode.Decompress); var reader = new StreamReader(decompress); string ret = reader.ReadToEnd(); return ret; } public T Get(params string[] ids) { var response = Request(Method + string.Join(",", strings); return Deserialize(response); } public T Deserialize(string response) { return JsonConvert.DeserializeObject(response); } } public class StackOverflow : IStackOverflow { IUserRepository _userRepo; public StackOverflow(IUserRepository userRepository) { _userRepo = userRepository; } public StackUser GetUserById(params string[] ids) { var users = _userRepo.GetUserById(ids); return users; } }
The StackRepository class here takes in a Generic class that will represent the type of Response that will be returned from the Api, such as the UserResponse that I created above. To use the StackOverflow object, just do this…
var stackOverflow = new StackOverflow(); var user = stackOverflow.GetUserById("558672");
So that is the slightly more complicated way to do it. It takes a little bit more setup, but it makes it a little bit easier to work with and makes the objects re-usable.
Have fun with JSON.net!
UPDATE 2/8/2012
As per a guest recommendation in the comments, there actually is a third way to deserialize the data.
var client = new WebClient(); var stackRequest = new StringBuilder(ApiUrl + request); byte[] response = client.DownloadData(stackRequest.ToString()); if(response == null) { throw new Exception(string.Format("No response from request {0}", request)); } var decompress = new GZipStream(new MemoryStream(response), CompressionMode.Decompress); var reader = new StreamReader(decompress); string ret = reader.ReadToEnd(); dynamic userResponse = JObject.Parse(ret); var displayName = userResponse.users[0].display_name; Console.Write(displayName);
So there you go, 3 ways to use JSON.net to work with a JSON Api!