Using Json.NET with MonoTouch

Published 11/21/2009 by brettnagy in iPhone, Mono
Tags: , ,

I make heavy use of external data in my iPhone applications and for that I rely on JSON. Our server-side data access moved to JSON a while ago as it allows us to support heterogeneous caller types, such as web browsers with JavaScript, server processes and (as seen here) mobile apps.

The latest version of Json.NET is an excellent library. There were a few gotchas for me, when getting it to work with MonoTouch.

1. Project Settings

After downloading the source code, add the Newtonsoft.Json project to your solution. Control+Click the project name and select 'Options'

From the General section, set the Runtime version to 'Mono for iPhone'

General Options Window

Next, from the Compiler section, add the SILVERLIGHT and PocketPC symbols. Be sure both are added to Debug and Release configurations.

Compiler Options Window

2. Using Json.NET in C#

The MonoTouch linker is insanely slick. It's clever enough to exclude unused code from the final, iPhone native executable. However, this can cause issues with reflection based code as come run time, methods and properties you clearly see in source code are just not present or detectable using reflection.

This caught me out at first, as during Json.NET deserialization none of my class properties were being bound to and Json.NET kept complaining that my classes had no default constructor, when they clearly did.

Of course, MonoTouch designers have already thought of this and have given us the MonoTouch.Foundation.Preserve attribute. Adding this to methods or properties will prevent the linker from removing them. The classes I now use for deserialization look (something) like this:

public class Avatar
{
	[MonoTouch.Foundation.Preserve]
	public Avatar(){}
		
	[MonoTouch.Foundation.Preserve, JsonProperty("url")]
	public string Url { get; set; }
		
	[MonoTouch.Foundation.Preserve, JsonProperty("size")]
	public string Size { get; set; }
}

Here is the code I use to access the external JSON data and download it into a string instance:

Uri address = new Uri(SERVICE_URL);
			
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
			
string responseBody = null;
try
{
	UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;
				
	using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)  
	{    
	    var reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);  
				    
	    responseBody = reader.ReadToEnd();  
	} 
}
catch ( WebException we )
{
	Console.WriteLine( we.Message );
}
finally
{
	UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false;
}

Now I can use something similar to the following code to take that string and deserialize it into my class instances:

List<Avatar> memberAvatars = JsonConvert.DeserializeObject<List<Avatar>>( responseBody );

I've been working on a set of native iPhone applications, developed using MonoDevelop and MonoTouch. Now that I have the first application running on a device (as opposed to the iPhone simulator), I plan on sharing the code for all to see.

As soon as I've chosen a good hosted source control provider (e.g GitHub, CodePlex, etc.) I'll include the link here.

Game Home Screen You've Won Screen


I'm working on an Asp.Net MVC application to provide an REST / JSON API to data in the Microgroove platform. WCF is overkill for us as this is meant to be a publically accessible, HTTP-based API. In other words, standard stuff.

There is a link to a very small MVC Visual Studio solution at the end of this entry. It simply combines the jQuery $.getJSON example with the standard MVC solution template. The homepage uses jQuery to get some JSON data and supplies a callback parameter:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
JSONP Example
</asp:Content>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2><%= Html.Encode(ViewData["Message"])%></h2>

<div id="images"></div>

<script type="text/javascript">
$(document).ready(function() {
$.getJSON("/feeds/getjson?jsoncallback=?",
function(data) {
$.each(data.items, function(i, item) {
$("<img/>").attr("src", item.media.m).appendTo("#images");
if (i == 3) return false;
});
});
});
</script>

</asp:Content>

Looking at the "/feeds/getjson" controller action, you'll see a new ActionResult derived class named "JsonpResult":

    /// <summary>
    /// An action result that renders the given object using JSONP to the response stream.
    /// </summary>
    public class JsonpResult : ActionResult
    {
        /// <summary>
        /// The result object to render using JSON.
        /// </summary>
        public object JsonData { get; set; }

        /// <summary>
        /// Gets or sets the callback handler.
        /// </summary>
        /// <value>The callback handler.</value>
        public string CallbackHandler { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="JsonpResult"/> class.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="callbackHandler">The callback handler.</param>
        public JsonpResult(object data, string callbackHandler)
        {
            JsonData = data;
            CallbackHandler = callbackHandler;
        }

        /// <summary>
        /// Enables processing of the result of an action method by a custom type
        /// that inherits from <see cref="T:System.Web.Mvc.ActionResult"/>.
        /// </summary>
        /// <param name="context">The context within which the result is executed.</param>
        public override void ExecuteResult(ControllerContext context)
        {
            context.HttpContext.Response.ContentType = "application/json";

            JavaScriptSerializer ser = new JavaScriptSerializer();
            string json = ser.Serialize(JsonData);
            string jsonp = CallbackHandler + "(" + json + ")";

            new ContentResult { Content = jsonp }.ExecuteResult(context);
        }
    }

I have used the standard .NET JavaScript serializer and then use the ContentResult helper to write the JSON back to the controler context. So, here's the link to the full thing:

JsonpExample.zip (250.93 kb)


Here's how we have been using model binder and UpdateModel of ASP.NET MVC to persist form data consisting of "Check all that apply" type questions.

The Model

This C# contains a Program class which in turn contains a list of Disciplines.

public class Program
{
    public virtual Guid ID { get; set; }
    public virtual IList<Discipline> Disciplines { get; set; }

    public Program()
    {
        Disciplines = new List<Discipline>();

        var discipline = new[] { "Dance", "Literary Arts", "Media" };

        foreach ( var detail in discipline )
        {
            Disciplines.Add( new Discipline { Title = detail } );
        }
    }
}

public class Discipline
{
    public virtual Guid ID { get; set; }
    public virtual string Title { get; set; }
    public virtual bool? IsSelected { get; set; }
}

The properties are marked virtual as we use NHibernate for the ORM. When a Program instance is created, we also pre-populate its collection of Disciplines. Now, it's true that from the relational point of view, this leads to what looks like a 2nd-normal form schema. But, that post is for another day...

The View

<% for (int i = 0; i < Model.Disciplines.Count; i++) {  %> 
<p>
    <%= Model.Disciplines[i].Title %><br />
    <input type="hidden" name="Disciplines.Index" value="<%= i %>" />
    <input type="hidden" name="Disciplines[<%= i %>].Title" value="<%= Model.Disciplines[i].Title %>" />
    <%= Html.RadioButton( "Disciplines[" + i + "].IsSelected", "true", Model.Disciplines[i].IsSelected ?? false ) %> Yes
    <%= Html.RadioButton( "Disciplines[" + i + "].IsSelected", "false", !(Model.Disciplines[i].IsSelected ?? true) ) %> No
</p>
<% } %>

You will see that we check for the null booleans, which is really a way to check whether the end user has selected Yes, No or nothing.

The Controller

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Program(FormCollection collection)
{
    var program = new Program();

    UpdateModel( program, new[]{ "Disciplines" } );

    //call our service to persist the new program
    _programService.SaveOrUpdate( program );

    return View( program );
}

 


Selfish Design

Published 6/27/2009 by brettnagy

I've been using the new iPhone 3.0 OS for just over a week now and of course, it's a huge improvement over previous versions. It's a hell of a lot faster (no wonder the iPhone 3G S feels fast) and adds a ton of features. The Stocks app, in particular, has been given quite an update; news headlines, more market stats, landscape mode and more interact chart modes. But, the Weather app? Nothing.

I can only assume this is due to the sun always shining in California where all the product managers, engineers and decision makers live. I live in Seattle and the sun definitely does not always shine, so I would have liked an update to the Weather app. Instead, I have to use the Weather Channel app, while good, carries inline ads.

This lead me to wonder, does Microgroove do the same thing with our product design and features?

There's no doubt our clients use our products more than we do, there's simply more clients than us. And Microgroove isn't in the Recorded Music Business as such (I'd say we're in the Music Software & Technology Business), so our clients must know more than we do about what they want?

The route we take is to ask our clients what features they want (they actually come to us before we reach out to them), but we decide how to realize that feature. If a client asks for a Video Playlist Feature, we decide how the CMS will enable our customers to organize their videos into playlists.

Then again, as Henry Ford said, "If I'd asked my customers what they wanted, they would have said 'faster horses'".


About 2 years ago (November 2007) I wrote a small JavaScript function to request JSON data from a URL and bind the results to a client-side, HTML-resident template. I used it a demo piece, showing that even though our platform product used (server-side) templates to render it's markup, those template could contain anything, such as JSON to push to a client. There are quite a few of these around now, but I'm still using my own as it's small, I understand how it works (I hope) and recently I used it as an excuse to get into jQuery plugin authoring, modifying my adhoc function to become a plugin. Here are the quickstart four steps to using it and a full example download can be found at the bottom of the page (as soon as I've uploaded it).

Step 1 : Include Required Libraries

<script type="text/javascript" src="/scripts/jquery-1.3.2.min.js">
<script type="text/javascript" src="/scripts/jquery.templatebinder.js">

Step 2 : Add Destination Markup

This basically means, when do you want the bound template contents to end up? I usually include an empty <div>.

<div id="template_destination"></div>

Be sure to include both opening and closing tags, so that the innerHtml can be targeted.

Step 3: Define Your Templates

The trick to including templates in HTML is to prevent the browser thinking they are HTML. Typically, templates are fragments of HTML and therefore unlikely to be compliant. For instance, partial tables, <TR> rows, etc. So, thanks to John Resig for this next tip:

<script type="text/html" id="table_header">
<table>
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>Date Of Birth</th>
</tr>
</thead>
<tbody>
</script>

<script type="text/html" id="tablerow_template">
<tr>
<td><span>{{Name}}</span></td>
<td><span>{{Address}}</span></td>
<td><span>{{DateOfBirth}}</span></td>
</tr>
</script>

<script type="text/html" id="table_footer">
</tbody></table>
</script>

The header and footer elements are optional.

Step 4 : Call the Binder

$("#template_destination").templateBinder({
url: "/data/feed.json",
template: "tablerow_template",
header: "table_header",
footer: "table_footer"
});

There are a few extra options that can be passed into the binder, such as whether the template destination is first emptied. Here is a link to the code (21.94 kb).


Brett Nagy

Software development, engineering and everything in between