Storage Files and JSON

Something I was totally unfamiliar was, how to deal with was the StorageFiles and JSON data in WinRT. This is a basic example of how to read and write JSON data into a StorageFile.

Before we get to the code. What is JSON?

MSDN defines JSON as

an open, text-based data exchange format (see RFC 4627). Like XML, it is human-readable, platform independent, and enjoys a wide availability of implementations. Data formatted according to the JSON standard is lightweight and can be parsed by JavaScript implementations with incredible ease, making it an ideal data exchange format for Ajax web applications. Since it is primarily a data format, JSON is not limited to just Ajax web applications, and can be used in virtually any scenario where applications need to exchange or store structured information as text.

So JSON is relatively simpler than XML. It provides easier format to save and retrieve data and has native support for Arrays and Objects. Data stored will be in the following key value format.

[{“FirstName”:”hello”,”SecondName”:”sir”}]

Lets get our hands dirty with code. 😀

I created a simple page with two textboxes and a button, We enter the first name and the second name and when we click the button called save, it should save the data as JSON data into a File.

Now we make one more copy of the above elements, ie 2 more textboxes and a button. This button is for retrieving the saved data. On press of this retrieve button, it should open the same file, read the data and put them into the newly created textboxes.

<Grid Margin="107,75,816,335" Grid.Row="1">

<TextBox x:Name="firstTextbox" HorizontalAlignment="Left" Height="40" Margin="155,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="140" FontSize="20" />

<TextBox x:Name="secondTextbox" HorizontalAlignment="Left" Height="45" Margin="155,65,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="140" FontSize="20" />
<TextBlock x:Uid="firstTextblock" HorizontalAlignment="Left" TextWrapping="Wrap" Text="First" VerticalAlignment="Top" Width="110" Height="40" FontSize="28"/>
<TextBlock x:Uid="secondTextblock" HorizontalAlignment="Left" TextWrapping="Wrap" Text="Second" VerticalAlignment="Top" Margin="0,75,0,0" Width="110" Height="35" FontSize="28"/>
<Button Content="Save JSON" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="80,145,0,0" Tapped="OnSaveButtonClicked"/>
</Grid>

<Grid Margin="640,75,316,335" Grid.Row="1" RenderTransformOrigin="0.5,0.5">
<TextBox x:Name="firstTextbox1" HorizontalAlignment="Left" Height="40" Margin="155,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="140" />
<TextBox x:Name="secondTextbox1" HorizontalAlignment="Left" Height="33" Margin="155,75,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="140" />
<TextBlock x:Uid="firstTextblock" HorizontalAlignment="Left" TextWrapping="Wrap" Text="First" VerticalAlignment="Top" Width="110" Height="40" FontSize="28"/>
<TextBlock x:Uid="secondTextblock" HorizontalAlignment="Left" TextWrapping="Wrap" Text="Second" VerticalAlignment="Top" Margin="0,75,0,0" Width="120" Height="35" FontSize="28"/>
<Button x:Name="RetrieveButton" Content="Retrieve" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="155,155,0,0" Tapped="OnRetrieveClicked"/>
</Grid>

Add the following namespace so as to enable the JSON data and declare a StorageFolder inside the MainPage class

using Windows.Data.Json;

Windows.Storage.StorageFolder localfolder;

So on the click of save button you call the SaveJSONData function.


private async void OnSaveButtonClicked(object sender, TappedRoutedEventArgs e)
 {
await SaveJSONData();
}

And the SaveJSONData has the code to convert the text to JSON. Here I have declared just one JsonObject. If you have an array of objects you can declare a JsonArray to which each object can be placed.

public async Task<bool> SaveJSONData()
 {
 JsonArray dataArray = new JsonArray();

JsonObject dataObject = new JsonObject();
 dataObject.Add("FirstName", JsonValue.CreateStringValue(firstTextbox.Text));
 dataObject.Add("SecondName", JsonValue.CreateStringValue(secondTextbox.Text));

dataArray.Add(dataObject);

string jsonString = dataArray.Stringify();
 await WriteToFile(jsonString);
 return true;
 }

JsonArray.Stringify returns the JSON representation of the encapsulated value. So after obtaining the encapsulated value we write it to a file.

public async Task<bool> WriteToFile(string jsonString)
 {
 try
 {
 StorageFile DataFile = await localfolder.CreateFileAsync("SampleFile", CreationCollisionOption.ReplaceExisting);

using (var stream = await DataFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
 {
 var outputStream = stream.GetOutputStreamAt(0);

using (DataWriter dataWriter = new DataWriter(outputStream))
 {
 dataWriter.WriteString(jsonString);

await dataWriter.StoreAsync();
 dataWriter.Dispose();
 await outputStream.FlushAsync();
 }

await stream.FlushAsync();
 }
 }
 catch (Exception)
 {
 }

return true;
 }

Similarly for reading the saved data,

private async void OnRetrieveClicked(object sender, TappedRoutedEventArgs e)
 {
 await RetrieveJSONData(); ;
 }

public async Task<bool> RetrieveJSONData()
 {
 string jsonString = await ReadFromFile();
 if (jsonString != null)
 {
 JsonArray jsonArray = JsonArray.Parse(jsonString);
 firstTextbox1.Text = jsonArray.GetObjectAt(0).GetNamedString("FirstName");
 secondTextbox1.Text = jsonArray.GetObjectAt(0).GetNamedString("SecondName");
 return true;
 }
 return false;
 }

 public async Task<string> ReadFromFile()
 {
 StorageFile DataFile = null;
 String buffer = null;

try
 {
 DataFile = await localfolder.GetFileAsync("SampleFile");
 }
 catch (FileNotFoundException)
 {
 }
if (DataFile != null)
 {
 try
 {
 using (var fs = await DataFile.OpenAsync(FileAccessMode.Read))
 {
 using (var inStream = fs.GetInputStreamAt(0))
 {

using (var reader = new DataReader(inStream))
 {
 await reader.LoadAsync((uint)fs.Size);
 buffer = reader.ReadString((uint)fs.Size);
 }
 }
 }
 return buffer;
 }
 catch (Exception)
 {
 }
 }
 return buffer;
 }

You have successfully created JSON data and written it to a file and read it back. Alternatively you can use serialize the .NET class objects to JSON and deserialize them back.

How to: Serialize and Deserialize JSON Data

Happy coding! 😀

Advertisements

Localization of a Metro app

While porting your app from Windows Phone to Windows 8, you’ll see that a lot of the code can be reused without much of trouble. Most of the changes have to be done in the design front as more screen real estate is available as compared to a standard 480×800.

Localization is defined by MSDN as

the translation of application resources into localized versions for the specific cultures that the application supports.

There are few differences in the way localization is done in Windows Phone and Windows 8. The resource file is named as a .resw file and not a resx file.  If you are porting the .resx as it is, I would suggest to name your elements the same as in the .resx file. Although you can localize using Bindings, I prefer associating the x:Uid which definitely makes the XAML code easier to read.

1. Firstly we need to create a folder in the project into which all the strings in the app needs to go. Here I have named it Strings

2. Inside the Strings folder, we create subfolders for each language. eg: en, fr.

3. Then we create individual resource file for every culture. [Right-Click on folder > Add > New Item > Resource File (.resw) .By convention name it as Resources.resw

After the above steps your solution explorer will look like this

Next is how we bind the xaml elements to these strings and how we can access the resw strings from the code-behind.

XAML

1. For every xaml element you create and has a string that need to be localized, associate a x:Uid attribute with it.

For eg,


<TextBlock x:Uid="calPageTitle" Grid.Column="1" Text="" Style="{StaticResource PageHeaderTextStyle}"/>

2. Now, in your Resource.resw file add the string that needs to appear in your TextBlock. Add the x:Uid of the element and the property. Here I’ve added the Text property ie the string should display “Calendar”.

3. We then set the Neutral language of the app to en-US.

To set the neutral language, Right Click the project in the Solution Explorer > Properties > In the Application Tab which is highlighted by default, click Assembly Information >In the neutral language option select the default language we want. Here I’m selecting English(United States)

Now when you run the app, the Textblock named pageTitle should have displayed “Calendar”.

For the other languages add the appropriate text in the corresponding resw.

Accessing the string from code behind

Declare a new object, loader of ResourceLoader type. You may have to include

using Windows.ApplicationModel.Resources;

Use GetString function to obtain the necessary string. I have noticed that the string in the resw which is bound to the xaml x:Uid cannot be used in the code behind. Some how string calPageTitle  or calPageTitle.Text does not seem to fetch the string. So I declare another string in the resw file called calPageTitle2 which has the same string “Calender”.

var loader = new ResourceLoader();
pageTitle.Text = loader.GetString("calPageTitle2")

So when I run the app once with the x:Uid removed , “Calender” is displayed again.

To test for different languages, change the simulator language from the control panel.

As in windows phone, there is no need of writing a separate class for fetching the strings and we do not need to make changes to the <SupportedCulture> in the cs.proj file.

🙂 I have my app localized!!

May the code be with you 🙂

Ref: Quickstart: Using string resources (Metro style apps using C#/VB/C++ and XAML).