Windows 8–Conference Buddy. Reading JSON From A File

I’d like to have a page in Conference Buddy that lists all the customer contacts.  We’re currently writing these contacts to file in JSON ListBoxCBformat as described in this article

My goal for today is to read that file and populate a ListView with the contents, using data binding.

To get started, I need to ensure that my JSON file is right.  Until now, I’ve been adding new records one by one to the file, but that won’t give me correct JSON.  The right solution is to keep a list of all the customers in memory, append to that list when new records are added and then write that list to file, so that we get the correct brackets, etc.

We can then read that file back into a collection class and bind to that class to populate a ListView.

Here’s the JSON file we want to end up with:

[{"Email":"Jesse.Liberty@Telerik.com","FirstName":"Jesse","LastName":"Liberty","Title":"XAML Evangelist","Company":"Telerik","PhoneNumber":"617-848-9684"},
{"Email":"Michael.Crump@Telerik.com","FirstName":"Michael","LastName":"Crump","Title":"XAML Evangelist","Company":"Telerik","PhoneNumber":"Unknown"},
{"Email":"StaceyLiberty@foo.com","FirstName":"Stacey","LastName":"Liberty","Title":"QA Specialist","Company":"Liberty Associates, Inc.","PhoneNumber":"617-848-9684"},
{"Email":"a","FirstName":"b","LastName":"c","Title":"d","Company":"e","PhoneNumber":"f"},
{"Email":"1","FirstName":"2","LastName":"3","Title":"4","Company":"5","PhoneNumber":"6"},
{"Email":"zz","FirstName":"yy","LastName":"xx","Title":"ww","Company":"vv","PhoneNumber":"uu"},
{"Email":"staceyliberty@verizon.net","FirstName":"Stacey","LastName":"Liberty","Title":"QA","Company":"LA","PhoneNumber":"978-635-5400"},
{"Email":"Phil@foo.com","FirstName":"Phil","LastName":"Japikse","Title":"Evangelist","Company":"Telerik","PhoneNumber":"617-555-1212"}]

The entire set of records begins and ends with square brackets. Each record is separated by a comma, and each record begins and ends with a curly brace. 

We’ll modify the code to write the records so that we’re writing out a collection, rather than each record one at a time. To do this, we add a property to the ViewModel,

 

private List<Customer> customers;
public List<Customer> Customers
{
get { return customers; }
set
{
customers = value;
NotifyPropertyChanged("Customers");
}
}

We can then append to this collection from the event handler when the user clicks the save button on the app bar,

private void Save_Click( object sender, RoutedEventArgs e )
{
Customer cust = new Customer();
cust.Company = Company.Text;
cust.Email = Email.Text;
cust.FirstName = FirstName.Text;
cust.LastName = LastName.Text;
cust.PhoneNumber = PhoneNumber.Text;
cust.Title = Title.Text;
_vm.Customers.Add(cust);
List<Customer> customers = _vm.Customers;

string JSON = JsonConvert.SerializeObject( customers );
Message.Text = JSON;
WriteToFile( JSON );

ClearFields();
}

JsonConvert will convert any .NET collection to a JSON array, which is just what we want.  We then write that array to the file, just as we previously wrote a single record to the file.  The one key difference is that when we create the file in the VM we are careful to set the CreationCollisionOption on the file to ReplaceExisting so that we are not appending one list onto another, but instead replacing the old list with the new list.

public async Task<StorageFile> CreateFile( string subFolderName, string fileName )
{
if ( CurrentFolder != null )
{
var folder = await CurrentFolder.CreateFolderAsync(
subFolderName, CreationCollisionOption.OpenIfExists );

return await folder.CreateFileAsync(
fileName, CreationCollisionOption.ReplaceExisting ).AsTask();
}
else
return null;
}

Opening the File and Reading

The code for opening the file is very similar to the code for creating the file.  First we make sure the CurrentFolder (that points to Documents) isn’t null.  We then call CreateFolderAsync on the subfolder, opening it if it exists (which it will).  Finally, we call GetFileAsync on that subfolder, passing in the name of the file we wish to open; what we get back is a StorageFile object for that file. 

public async Task<StorageFile> OpenFile(string subFolderName, string fileName)
{
if (CurrentFolder != null)
{
var folder = await CurrentFolder.CreateFolderAsync(
subFolderName, CreationCollisionOption.OpenIfExists);

//TODO: Handle if file does not exist
return await folder.GetFileAsync(fileName);
}
else
return null;
}

We create a method ReadFromFile in our code behind which we will call to obtain the JSON string so that we can populate the list box.  Its job is to call the ViewModel’s LoadDocumentsLibrary to set up the read and then to call the OpenFile method with the correct file names. Once we have the file open we can call ReadTextAsync on the FileIO object, passing in the name of the file and getting back its contents as a string. 

private async Task<string> ReadFromFile()
{
string folderName = "ConferenceBuddy";
string fileName = "ConferenceBuddy.json";
_vm.LoadDocumentsLibrary();
var file2 = await _vm.OpenFile(folderName, fileName);
string fileContents = string.Empty;
if (file2 != null)
{
fileContents = await FileIO.ReadTextAsync(file2);
}

return fileContents;
}

We want to populate the list box as soon as we get to the page, so we add a call to PopulateListBox in the OnNavigatedTo method.  PoopulateListBox just calls ReadFromFile and then asks JsonConvert to Deserialize the object back into a collection.  We then set the View Model’s Customers property to that collection,

private async void PopulateListBox()
{
var json = await ReadFromFile();
List<Customer> customers = JsonConvert.DeserializeObject<List<Customer>>(json);
_vm.Customers = customers;
}
 
If you provide JsonConvert.DeserializeObject with a list type it will parse the json array into that .Net collection type.
 

The list box needs to know how to display each record and so we use an ItemTemplate,

<StackPanel Margin="100">
<ScrollViewer>
<ListView Name="xCustomers"
ItemsSource="{Binding Customers}"
Height="400">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding LastName}" />
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</StackPanel>

There is considerably more we can and will do to make this look better, but the essential capabilities are now in place to persist our collection to file and to read it from a file into a ListView or other collection display.

Share

About Jesse Liberty

Jesse Liberty is a Master Consultant for Falafel Software, and has three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been a Technical Evangelist for Telerik and for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS.
This entry was posted in Data, Essentials, Mini-Tutorial, Windows 8 and tagged . Bookmark the permalink.

3 Responses to Windows 8–Conference Buddy. Reading JSON From A File

  1. Nate says:

    Yeah same with me, could not get the list to work… Good other wise! Thanks!

  2. musbah says:

    This was pretty helpful except that I wasn’t able to get the writing to list part to work out correctly, kept getting some error about a variable being null…

    • I believe the problem you are having happens when there is no initial file – no json to read. The fix is in two steps:

      1) In MainPage.xaml.cs in the OnNavigatedTo method, change

      JsonConvert.DeserializeObject<List>( json );

      to

      JsonConvert.DeserializeObject<List>( json )
      ?? new List();

      2) in ViewModel.cs place the call to CreateFolderAsync into a try/catch block:

      try
      {
      var folder = await CurrentFolder.CreateFolderAsync(
      subFolderName, CreationCollisionOption.OpenIfExists);

      return await folder.GetFileAsync(fileName);
      }
      catch
      {
      // Unfortunately necessary because GetFileAsync returns an exception
      return null;
      }

      [Thanks Jon G!]

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>