JSON (JavaScript Object Notation) is a widely used language-independent lightweight data format that's used both for storing and interchanging data between applications.
Haxe includes built-in tools for parsing and encoding data in JSON format, which I'm going to cover in this tutorial.
JSON is a text format, so raw data is first loaded into a String object, which is then parsed by the haxe.Json class to produce a Dynamic object. If you already know what the structure of the JSON is going to be, you can produce a custom typed object instead.
Take a look at the example below:
First I create a simple JSON string, in this case it's an object with two properties, one of which is a String, and one of which is an Array of Strings.
var string:String = '{"date": "2015-05-06", "food":["bacon", "tomatoes", "apples"]}';
Make sure you import the Json class before moving to the next step:
import haxe.Json;
Let's try parsing this string into a Dynamic object. For the record - a Dynamic object has no set structure and can have any value of any type assigned to it. Our JSON can be parsed using the Json.parse() method:
var object:Dynamic = Json.parse(string);
trace("Object: " + object);
trace("Date: " + object.date);
trace("Food: " + object.food);
trace("First element: " + object.food[0]);
Tracing will output the following values:
Object: { date => 2015-05-06, food => [bacon,tomatoes,apples] }
Date: 2015-05-06
Food: [bacon,tomatoes,apples]
First element: bacon
Great, we've parsed our data and can now access it like a regular Haxe object.
As I already said, this produces a Dynamic object, which may be convenient but not always the best solution. Whenever possible, it is best to type all of your objects. If we know the exact structure of our received JSON data, we can create a custom type for it.
typedef ShoppingList = {
var date:String;
var food:Array<String>;
}
Now I will use the same parse() method as before, except assign the returned value to a ShoppingList typed variable:
var list:ShoppingList = Json.parse(string);
trace("Shopping list: " + list);
trace("Date: " + list.date);
trace("Food: " + list.food);
trace("First element: " + list.food[0]);
The output will be the same:
Object: { date => 2015-05-06, food => [bacon,tomatoes,apples] }
Date: 2015-05-06
Food: [bacon,tomatoes,apples]
First element: bacon
This is a better approach because the compiler knows about the expected object structure and will attempt to optimize generated code.
To turn an object into a JSON String, you can use the stringify() method:
trace(Json.stringify(list));
This will return the same JSON String that we had before the parsing:
{"date":"2015-05-06","food":["bacon","tomatoes","apples"]}
You can pretty-print the output by assigning a value to the third parameter of the stringify() method - space. The entered string value will be used as indentation when printing hierarchy-like JSON structure:
trace(Json.stringify(list, null, " "));
The output has the same content but now looks readable:
{
"date": "2015-05-06",
"food": [
"bacon",
"tomatoes",
"apples"
]
}
The second parameter of the stringify() method is a replacer function, which lets you customize the encoding process. The function receives a key-value pair that was supposed to be encoded into the object, and returns the value that will replace the initially offered value.
A replacer function can look like this:
private function replacer(key:Dynamic, value:Dynamic):Dynamic {
if (key == "date") {
return Date.now().toString();
}
return value;
}
Pass it to the stringify() method as the second argument:
trace(Json.stringify(list, replacer, " "));
As a result of this example, we will end up with a JSON object which has its date field set to current time.
{
"date": "2015-05-06 18:55:43",
"food": [
"bacon",
"tomatoes",
"apples"
]
}
Note that the raw JSON has to strictly follow the proper JSON format.
If you need a more tolerant JSON parser, take a look at the TJSON library on GitHub.