Tuesday, February 18, 2014

Using RestSharp to Deserialize JSON Simple Examples


RestSharp is a .NET REST client that can be downloaded and used to easily serialize and deserialize both XML and JSON.  If necessary, it will used fuzzy element name matching to map from the original JSON object to C# so that if an exact property name match isn't found it will use the following precedence to deserialize the data.


  1. Exact property name match (e.g. LastName -> LastName)
  2. Camel-cased version of the property name (e.g. LastName -> lastName)
  3. Lower-cased version of the property name (e.g. LastName -> lastname)
  4. Lower-cased version of "underscored" name (e.g. Last_Name -> last_name)


Using Xamarin Studio, I've included RestSharp from the component store in my C# project.  Here are a few simple examples of how to deserialize JSON into C#.

A Simple Dictionary Key Value such as {"Count":234}
// Create a new RestClient and RestRequest

            var client = new RestClient("http://www.atavisticsoftware.com/");
            var request = new RestRequest ("demo/jsondbcount.php",Method.GET);
// ask for the response to be in JSON syntax
            request.RequestFormat = DataFormat.Json

//send the request to the web service and store the response when it comes back
            var response = client.Execute (request);
// The next line of code will only run after the response has been received
// Create a new Deserializer to be able to parse the JSON object
            RestSharp.Deserializers.JsonDeserializer deserial= new JsonDeserializer();
//To deserialize into a simple variable, use the <Dictionary<string, string>> type
            var JSONObj = deserial.Deserialize<Dictionary<string, string>>(response);              int rowCount =  JSONObj["Count"]; //rowCount will be 234 based on the example {"Count":234}





To deserialize an object such as {"FirstName":"Eileen","LastName":"Dover"}
    public class People 
    {
        public string FirstName { get; set; }
        public string LastName { get; set;}
     }
. . . // (same as first example)
//To deserialize into an object, change the deserialize code to use the <Class> type
//Fuzzy matching is used to map the properties from the JSON object to the C# object properties
People people =  deserial.Deserialize<People> (response);  
//people.FirstName will now be "Eileen"  people.LastName will now be "Dover"

A collection of objects will come in array format such as:


[{"FirstName":"Eileen","LastName":"Dover"},{"FirstName":"Dan","LastName":"Druff"},{"FirstName":"Anita","LastName":"Drink"}]

    public class People 
    {
        public string FirstName { get; set; }
        public string LastName { get; set;}
     }

   public List<People> peopleList;
. . . // (same as first example)
//Change the deserialize code to type <List<Class>>
   peopleList = deserial.Deserialize<List<People>>(response);
//The list of People objects will be populated and the property names matched

In the above examples, the deserialization happens synchronously, so that the application will wait until a response is received before continuing.  RestSharp can also create asynchronous processing so that the application will continue execution without having received a response, but will handle the response from the web service when it arrives without holding up execution of other program logic.  This is especially useful to avoid blocking the UI thread in a mobile app while waiting for the web service and run the risk that the OS will terminate your application.

To make the above example run asynchronously, change the original client.Execute to the following:    
                              
                          . . . // (same as first example)

  client.ExecuteAsync (request, response => {

                RestSharp.Deserializers.JsonDeserializer deserial = new JsonDeserializer();
 
                peopleList = deserial.Deserialize<List<People>>(response);

            });



//The list of People objects will be populated when the response is finally received from the web service, but code immediately following this will run right away and not be held up waiting for the response

Sunday, February 9, 2014

ListView Using ActivityListItem Style & Image w/ onListItemClick Event


In this post we'll modify the ListView to use the ActivityListItem style.  ActivityListItem is another built-in view which will display an image and a single line of text for each row.

The People class will now have a property added that provides the image name to be displayed.

  class People 
    {
        public string FirstName { get; set; }
        public string LastName { get; set;}
        public string ImgURL { get; set; }
    }



The helper method which provides the data will populate a List of People objects with the values we want to display. (In a later post, we'll modify this to pull the data from a web service rather than hard-coding it).

        private List<People> PopulatePeopleData() {
            // this is just done in this demo for simplicity to create the People objects
            return new List<People> ()
            {

                new People { FirstName = "Anita", LastName = "Drink" , ImgURL = "images/anita.jpg"},
                new People { FirstName = "Amanda", LastName = "Reconwith", ImgURL = "images/amanda.jpg" },
                new People { FirstName = "Warren", LastName = "Peace", ImgURL = "images/Warren.jpg" },
                new People { FirstName = "Rhonda", LastName = "Corner" , ImgURL = "images/Rhonda.jpg"},
                new People { FirstName = "Karen", LastName = "Feeding" , ImgURL = "images/Karen.jpg"},
                new People { FirstName = "Rufus Lee", LastName = "King", ImgURL = "images/Rufus.jpg" },
                new People { FirstName = "Winston", LastName = "Payne" , ImgURL = "images/Winston.jpg"},
                new People { FirstName = "Marian", LastName = "Haste" , ImgURL = "images/Marian.jpg"},
                new People { FirstName = "Augusta", LastName = "Wind", ImgURL = "images/Augusta.jpg" },
                new People { FirstName = "Ben", LastName = "Dover", ImgURL = "images/Ben.jpg" },
                new People { FirstName = "Dan", LastName = "Druff", ImgURL = "images/Dan.jpg" },
                new People { FirstName = "Dick", LastName = "Tator", ImgURL = "images/Dick.jpg" },
                new People { FirstName = "Marty", LastName = "Graw", ImgURL = "images/Marty.jpg" },
                new People { FirstName = "Lauren", LastName = "Order", ImgURL = "images/Lauren.jpg" },
                new People { FirstName = "Les", LastName = "More", ImgURL = "images/Les.jpg" },
                new People { FirstName = "Molly", LastName = "Keull", ImgURL = "images/Molly.jpg" },
                new People { FirstName = "Rhoda", LastName = "Booke", ImgURL = "images/Rhoda.jpg" },
                new People { FirstName = "Rich", LastName = "Feller", ImgURL = "images/Rich.jpg" },
                new People { FirstName = "Holly", LastName = "Day", ImgURL = "images/Holly.jpg" },
                new People { FirstName = "Polly", LastName = "Ester", ImgURL = "images/Polly.jpg" },
                new People { FirstName = "Paige", LastName = "Turner", ImgURL = "images/Paige.jpg" },
                new People { FirstName = "Neil", LastName = "Down", ImgURL = "images/Neil.jpg" },
                new People { FirstName = "Jack", LastName = "Pott", ImgURL = "images/Jack.jpg" },
                new People { FirstName = "Miles", LastName = "Long", ImgURL = "images/Miles.jpg" },
                new People { FirstName = "Joy", LastName = "Kil", ImgURL = "images/Joy.jpg" }

            };
        }




The images are stored in the images folder under the Assets folder in the project.  Make sure to use a lower case "i"on the images folder or Android may not be able to locate it.  Also make sure that any images in the folder are set to build action: AndroidAsset by right-clicking on the image or the compiler will throw an error.  AndroidAssets will be included in the package when it is compiled.




In the data adapter PeopleAdapter.cs, layout style is set to ActivityListItem.  In this style, only Text1 will have data, so we won't need the Text2 used in the previous examples.  


        public override View GetView (int position, View rowView, ViewGroup parent)
        {

            // the rowView that is being filled may, or may not exist.  If not, inflate a new one, 
            //  otherwiser reuse the one passed in.
            var view = rowView;
            if (view == null) {
                // ListActivities have built in listviews that fill the whole screen.  There is no need for an AXML Layout
                // SimpleListItem2 is a predefined style with 2 text views, the first being bolder than the second
                //   no special AXML is required if this style is adequate
                view = context.LayoutInflater.Inflate (Android.Resource.Layout.ActivityListItem, null);
            }

            var people = data [position];  //get the people object that corresponds to this position

            // Only the first Text1 TextView can be used on an ActivityListItem
            view.FindViewById<TextView> (Android.Resource.Id.Text1).Text = people.LastName;
//            view.FindViewById<TextView> (Android.Resource.Id.Text2).Text = people.FirstName;

            // Get a reference to the built-in Icon ImageView 
            ImageView img = view.FindViewById<ImageView> (Android.Resource.Id.Icon);
            // set the image using the SetImageDrawable method. We'll use a helper function in a static class
            // to return the image based on the the ImgURL from the people object
            img.SetImageDrawable(GetImage.GetImageFromURL(people.ImgURL, context));

            return view;  // return the formatted view

        }


GetImage.GetImageFromURL will be called from a static class to retrieve the drawable image from the assets and return it to be inserted in the row view.  This method will also be used later to display the image in a full screen view so the calling Activity is passed in as the context.



    static class GetImage
    {

        public static Drawable GetImageFromURL(string url, Activity context) 
        {

            Drawable headshotDrawable = null;
            try 
            {
                headshotDrawable = Drawable.CreateFromStream(context.Assets.Open(url), null);
            }
            catch (Exception ex) 
            {         
                     headshotDrawable = Drawable.CreateFromStream(context.Assets.Open("images/photo.png"), null);
            }
            return headshotDrawable;
        }
    }



To activate the onClick event when the user clicks on one of the rows, we'll override OnListItemClick
in MainActivity.cs.  A list of strings from the people object that corresponds to the position that was clicked will be added to the list to be passed in the intent to a new activity will will display a full screen of information.  The data adapter PeopleAdapter instance people used by the MainActivity will return the corresponding people object when passed the position on the screen

public class MainActivity : ListActivity
    {
        PeopleAdapter people; 

  protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);

            // create a new instance of the PeopleAdapter and pass a reference to this activity so that the adapter
            // can inflate new rows in the in the built-in ListView that comes with a ListActivity class
            people = new PeopleAdapter (this);
            ListView.Adapter = people;
        }

    protected override void OnListItemClick(ListView l, View u, int position, long id)
        {             
            List<string> pList = new List<string>();
            pList.Add(people[position].FirstName);
            pList.Add(people[position].LastName);
            pList.Add(people[position].ImgURL);

            var intent = new Intent (this, typeof(NewActivity));

            intent.PutStringArrayListExtra ("People", pList);
            StartActivity (intent);
        }



The new activity will retrieve the list of strings passed in the intent and populate a new AXML layout.  It will also use the GetImage.GetImageFromURL created above to retrieve the image from the Assets/images folder.


    [Activity (Label = "NewActivity",Theme = "@android:style/Theme.NoTitleBar")]            
    public class NewActivity : Activity
    {
        protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);

            SetContentView (Resource.Layout.People);

            var pList = Intent.Extras.GetStringArrayList ("People") ?? new string[0];

            TextView fn = FindViewById<TextView> (Resource.Id.FirstName);
            TextView ln = FindViewById<TextView> (Resource.Id.LastName);
            fn.Text = pList [0];
            ln.Text = pList [1];
            string pic = pList [2];

            ImageView img = FindViewById<ImageView> (Resource.Id.Picture);
            img.SetImageDrawable(GetImage.GetImageFromURL(pic, this));

        }
    }



The following screen is displayed by the NewActivity when the corresponding row is clicked by the user.