Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

Windows 8 Apps - Create objects from a JSON file

5.00/5 (1 vote)
26 Nov 2012CPOL6 min read 14.7K  
Create objects from a JSON file.

As I have been looking around in Windows Store and checking out what kind of apps exist I saw that there a lot of apps which have their content installed on the machine and are not loading it from the web. So even if you don't have internet access you can fully use the app. I though to write a blog post about loading a file in a Windows 8 app and creating objects from the data stored in the file. I will focus on json data, because it's the most commonly used format.

I will present a very simple project (it will not have any change in the UI), but I will try to present a structural guideline how the data and objects can be logically grouped in a Windows 8 WinJS app.

You can find the sample project on github

Let's say we have our zoo, and some animals were transferred from another zoo. All the data about the animals is sent via a zoo.json file, which looks like this: 

JavaScript
[{
    "name" : "King",
    "age" : 5,
    "hoursSinceLastFeed" : 3
},
{
    "name" : "Geeko",
    "age" : 2,
    "hoursSinceLastFeed" : 12
},
{
    "name" : "Nerdy",
    "age" : 12,
    "hoursSinceLastFeed" : 1
},
{
    "name" : "Goocy",
    "age" : 4,
    "hoursSinceLastFeed" : 6
}]  

This is the most simple set of information about an animal, Name, Age and the hours passed since the last feeding - probably this is the most important one :). This zoo.json file is which we would like to import. So, what I have done was, created a new WinJS application from the Blank Template:

Create a new Blank WinJS project
Create a new Blank WinJS project

The next step was, that I added a new JavaScript file, called Animal.js.

JavaScript
/// <reference path="//Microsoft.WinJS.1.0/js/base.js" /> 
/// <reference path="//Microsoft.WinJS.1.0/js/ui.js" /> 


(function () {
    "use strict";

    WinJS.Namespace.define("Zoo", {
        Animal: WinJS.Class.define(

            //constructor
            function () {
                this._name = "";
                this._age = "";
                this._isHungry = false;
                this._hoursSinceLastFeed = 0;
            },

            //methods
            {
                getName: function () { return this._name; },
                setName: function (newValue) { this._name = newValue; },

                getAge: function () { return this._age; },
                setAge: function (newValue) { this._age = newValue; },

                isHungry: function () { return this._isHungry; },

                getHoursSinceLastFeed: function () { return this._hoursSinceLastFeed; },
                setHoursSinceLastFeed: function (newValue) {
                    this._hoursSinceLastFeed = newValue;
                    //if it has been 4 hours since last feed
                    //the animal is hungry for sure :)
                    if (newValue > 4) {
                        this._isHungry = true;
                    }
                    else {
                        this._isHungry = false;
                    }
                },
            },

            //static methods
            {
                buildAnimal: function (model) {

                    var newAnimal = new Zoo.Animal();

                    if (model.hasOwnProperty("name")) {
                        newAnimal.setName(model.name);
                    }

                    if (model.hasOwnProperty("age")) {
                        newAnimal.setAge(model.age);
                    }

                    if (model.hasOwnProperty("hoursSinceLastFeed")) {
                        newAnimal.setHoursSinceLastFeed(model.hoursSinceLastFeed);
                    }

                    //return a Bindable object
                    return new WinJS.Binding.as(newAnimal);
                },

                loadZoo: function (uri) {
                    //IMPORTANT TO RETURN THE PROMISE
                    return Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri)
                        .then(function (file) {
                            return Windows.Storage.FileIO.readTextAsync(file)
                                .then(function (textFromFile) {
                                    var myParsedJsonData = JSON.parse(textFromFile);

                                    //this will store all the new animals transferred to zoo
                                    var zoo = new Array();

                                    if (myParsedJsonData) {
                                        myParsedJsonData.forEach(function (newObject) {
                                            var newAnimal = Zoo.Animal.buildAnimal(newObject);
                                            zoo.push(newAnimal);
                                        });
                                    }

                                    return zoo;
                                });
                        });
                }
            })//end  WinJS.Class.define
    });
})();

In this js file I created the object creation and parsing logic. This is the main file which this blog is about. I started the whole process with declaring a self executing function (if you don't know what are those, here are the first second and third hints which Google gave back when searching for JavaScript self executing functions). I added the "use strict" line, see my previous blog posts why this should be used.

After that comes the interesting part. I said, I will create a new object, inside the namespace Zoo, and that object contains one property, Animal. The Animal is defined as a WinJS.Class using the WinJS.Class.define() method. As it can be seen in the code, this method takes 3 arguments, the first one is a function, this serves as the constructor for the class. The second and the third parameters are objects. The object defined as the second parameter defines the methods which the newly created Animal objects will have:

As you can see, Visual Studio 2012 offers a very good intellisense support for JavaScript code. In the constructor I assigned some members to this(so later I can access them). After the definition of the constructor, I defined the methods which will help me in maintaining data integrity and encapsulation for this javascript object. There are defined get and set methods for each property. The isHungry member is treated separately, this has some logic behind the scenes. I said, that if more than 4 hours passed since the last feeding the animal is hungry. All this "complex" logic is implemented inside the setHoursSinceLastFeed() method.
In the third parameter of the WinJS.Class.define() method I specified 2 static methods:

C#
buildAnimal: function (model) {

    var newAnimal = new Zoo.Animal();

    if (model.hasOwnProperty("name")) {
        newAnimal.setName(model.name);
    }

    if (model.hasOwnProperty("age")) {
        newAnimal.setAge(model.age);
    }

    if (model.hasOwnProperty("hoursSinceLastFeed")) {
        newAnimal.setHoursSinceLastFeed(model.hoursSinceLastFeed);
    }

    //return a Bindable object
    return new WinJS.Binding.as(newAnimal);                 },

The buildAnimal function is a simple one. All it does is, checks the model/data passed in as parameter, creates a new Animal object, tries to set it's values from the model passed to the function and returns a bindable object with the help of WinJS.Binding.as() method.

The loadZoo function contains all the code which this blog post is created for. Here is the code:

JavaScript
loadZoo: function (uri) {
    //IMPORTANT TO RETURN THE PROMISE
    return Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri)
        .then(function (file) {
            return Windows.Storage.FileIO.readTextAsync(file)
                .then(function (textFromFile) {
                    var myParsedJsonData = JSON.parse(textFromFile);

                    //this will store all the new animals transferred to zoo
                    var zoo = new Array();

                    if (myParsedJsonData) {
                        myParsedJsonData.forEach(function (newObject) {
                            var newAnimal = Zoo.Animal.buildAnimal(newObject);
                            zoo.push(newAnimal);
                        });
                    }

                    return zoo;
                });
        });
}

The purpose of this function is, to load the file with the json data and create Animal type objects from it. First thing to notice, the function starts with the return statement, this is important and I'll get back to it. The function receives a URL to the file which contains the data. The line Windows.Storage.StorageFile.getFileFromApplicationUriAsync() accesses the file asynchronously. The method getFileFromApplicationUriAsync() returns a promise, which is chained with the then() method. The .then() method can have 3 parameters (more specifically functions). The first one should be the one which is called when the async operation is finished. The second function parameter stands in the role of an error handler. It is not mandatory to specify this parameter. If you have multiple .then() methods chained, if an error occurs this is escalated till in one of the then() methods an error handler is passed to.

Another scenario is when the invoke chain ends with a .done(), this can also have an error handler as second parameter. If for some reason the error handler function is not specified at all and an error occurs the Windows 8 app will crash (of course if no other error handling mechanism is added). The third parameter of the then() method serves as a progress indicator, more on this in a future post. The getFileFromApplicationUriAsync() returns a promise, the result (file object) of this promise is passed to the function inside the then() method. This object is then accessed and the method WinJS.Storage.FileIO.readTextAsync() reads all the text from the file. This method on it's own returns another promise, and the text read from the file is passed to the function specified for the onCompleted parameter of the then() method. This text is then parsed with the JSON.parse() method. This creates objects from the text passed to it, we can say, that "deserializes" the data. Afterwards a new array is built for results. For each item returned by the JSON.parse() method a new Animal object is created using the buildAnimal static method.

That was all the "science"! We know have a new, fully functional JavaScript class, which implements the factory design pattern and has the feature to load it's own datatype from files and build up it's own objects.

There remained two things to mention. The first is related to the URL passed to the loadZoo function. The URL has to be created with Windows.Foundation.Uri() method, see the code below:

JavaScript
args.setPromise(WinJS.UI.processAll().then(function() {
               
    //build up the URL for the file added to the project
    var url = new Windows.Foundation.Uri("ms-appx:///zoo.json");

    //this will store the imported data
    var myNewAnimals = new Array();

    //invoke the static method which loads the file
    //and creates Animal objects from json data
    //THE METHOD RETURNS A PROMISE!!!
    Zoo.Animal.loadZoo(url).done(

        function (result) {
            myNewAnimals = result;

            myNewAnimals.forEach(function (animal) {
                console.log("Name: " + animal.getName() + ", Age: " + 
                  animal.getAge() + ", IsHungry: " + animal.isHungry() + 
                  ", Hours since Last feed: " + animal.getHoursSinceLastFeed());
            });
        },
        function (error) {
            var messDialog = new Windows.UI.Popups.MessageDialog(error);
            messDialog.showAsync();
        });

})); 

The parameter given to the Windows.Foundation.Uri() method is a little strange. I will not go in-depth why this has to be written like this, in a future post I will try to present this also.  You can figure out that the last / from the ms-appx:///zoo.json points to the root of the local project and zoo.json is the name of the processed file. So if you search for ms-appx:// on the web you'll get a lot of details.

Remember the loadZoo method started with a return statement? That was done like that, because all the operations done were executed asynchronously, so I had to be able to chain my logic to the async operations and had to execute it after all the other async logic was executed. So, when the Zoo.Animal.loadZoo(url) finished it's execution in the done() method I write the processed data to the Visual Studio console and it looks like this:

You can do anything with these objects, bind them to controls on UI, serve for contracts (Search or Share) and so on.

Thanks for reading the post, hope you enjoyed it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)