﻿# Foundations JSON API

API | Description
--- | ---
query | Perform query on JSON data object
Schema.validate | Validate a data object as valid JSON and, optionally, against a JSON schema object.
Schema.checkPropertyChange | Checks an object property for a valid value given its schema.
Transformer.initialize | Register a template for transforming JSON data objects.
Transformer.merge | Merge two different JSON data objects.
Transformer.transform | Transform JSON data object(s) according to a set of named transforms in a registered template.
Transformer.transformAndMerge | Transform JSON data according to a set of named transforms in a registered template and merge it with other JSON data.

## Including foundations.json in Your Code

To use foundations.json in your code:

- Include the following two script tags in your app's Index.html file:

  >     <script src="/usr/palm/frameworks/mojo/mojo.js" type="text/javascript"
  >       x-mojo-version="1"></script>
  >     <script src="/usr/palm/frameworks/mojoloader.js" type="text/javascript"></script>

- Load and reference `foundations.json` in your JavaScript:

  >     var jsonlib = MojoLoader.require({name:"foundations.json", version: "1.0"});
  >     var fjson = jsonlib["foundations.json"];

You can then reference methods using standard dot notation. For example (`Schema.validate`):

>     Mojo.Log.info("FoundJSON: BadObj validate "+ JSON.stringify(fjson.Schema.validate(BadObj, jsonSchema)));

## query

Performs a JSONQuery on a provided object and returns the results. This takes the form:

>     results = query(searchQuery, object);

If no object is provided (just a query), it returns a "compiled" function that evaluates objects according to the provided query. Compiled functions take the following form:

>     evaluator = query(searchQuery);
>     results = evaluator(object);

JSONQuery provides a comprehensive set of data querying tools including filtering, recursive search, sorting, mapping, range selection, and powerful expressions with wildcard string comparisons and various operators. JSONQuery generally supersets JSONPath and provides syntax that matches and behaves like JavaScript where possible.

### Supported Operators

JSONQuery evaluations begin with a provided object, which can be referenced with `$`. From the starting object, various operators can be successively applied, each operating on the result of the last operation.

You can explicitly begin your query with `$`, but it is not necessary, as it is implicitly auto-inserted. This allows you to start queries with operators. JSONQuery uses syntax that is similar to JavaScript (with a number of extra operators).

A simple query looks like this:

>     var data = {foo:"bar"};
>     var results = query("$.foo",data);
>     // results == "bar"

For the code samples in the table below, assume the following initializations and data structure:

>     var jsonlib = MojoLoader.require({name:"foundations.json", version: "1.0"});
>     var fjson = jsonlib["foundations.json"];
>     var ItemPrices = [{ "Item" : "drink", "price" : 2},
>       { "Item" : "jacket", "price" : 22},
>       { "Item" : "shoes", "price" : 17},
>       { "Item" : "baseball", "price" : 6},
>       { "Item" : "bat", "price" : 4},
>       { "Item" : "book", "price" : 12}
>       { "Item" : "BELL", "price" : 10}];

Operator | Description | Example/Output
--- | --- | ---
.property | Returns the indicated object property, behaving exactly like JavaScript. |
    Mojo.Log.info("results = "+
                   fjson.query(".foo",{foo:"bar"}));
    results = bar
[expression] | Returns the property name/index defined by the expression evaluation, behaving exactly like JavaScript. | Get the value of Item for the second element:
    Mojo.Log.info("FoundJSON : results = "+fjson.query("\[1\].Item",
                   ItemPrices));
    results = jacket
[?expression] | Returns array items matching the expression. This operator does not need to be in brackets, you can simply use ?expression, but without "containment", no operators can be used afterwards. | Get elements whose price is greater than 7:
    Mojo.Log.info("results = "+
                   JSON.stringify(fjson.query("\[?price > 7\]",
                   ItemPrices)));
    results = [{"Item":"jacket","price":22},
               {"Item":"shoes","price":17},
               {"Item":"book","price":12},
               {"Item":"BELL","price":10}]
[^?expression] | Same as [?expression] except that duplicate values/objects are removed from the result set |
[/expression], [], | Indicates a sort operation ("/" = ascending and "" = descending). Multiple comma-delimited sort expressions are possible for multiple sort orders (first being highest priority), i.e., `[/LastName,/FirstName]`. | Get items whose price is greater than 7, sort by Item:
    Mojo.Log.info("results = "+
                   JSON.stringify(fjson.query("\[?price > 7\]\[\/Item\]",
                   ItemPrices)));
    results = [{"Item":"BELL","price":10},
               {"Item":"book","price":12},
               {"Item":"jacket","price":22},
               {"Item":"shoes","price":17}]
[=expression] | Performs a map operation on an array, creating a new array with each item being the evaluation of the expression for each item in the source array. | Create an array of prices from ItemPrices:
    Mojo.Log.info("results = "+
                   JSON.stringify(fjson.query("\[=price\]",
                   ItemPrices)));
    results = [2,22,17,6,4,12,10]
[start:end:step] | Performs an array slice/range operation, returning the elements from the optional start index to the optional end index, stepping by the optional step number. | Get every other element:
    Mojo.Log.info("results = "+
                   JSON.stringify(fjson.query("\[0:7:2\]",
                   ItemPrices)));
    results = [{"Item":"drink","price":2},
               {"Item":"shoes","price":17},
               {"Item":"bat","price":4},
               {"Item":"BELL","price":10}]
[expr,expr] | This a union operator, returning an array of all the property/index values from the evaluation of the comma delimited expressions. |
.* or [*] | This returns the values of all the properties of the current object. |
$ | This is the root object, If a JSONQuery expression does not being with a $, it will be auto-inserted at the beginning. |
@ | This is the current object in filter, sort, and map expressions. Note that names are auto-converted to property references of the current object in expressions, but @ can be used for index access on the current object. The following queries are identical:
    [?name='Fred']
    [?@.name='Fred']
    [?@['name']='Fred']
..property | Performs a recursive search for the given property name, returning an array of all values with such a property name in the current object and any sub-objects. |
expr = expr | Performs a comparison (like JS's ==). When comparing to a string, the comparison string may contain wildcards * (matches any number of characters) and ? (matches any single character). | Get all Items beginning with lowercase "b":
    Mojo.Log.info("results = "+
                   JSON.stringify(fjson.query("\[?Item='b*'\]",
                   ItemPrices)));
    results = [{"Item":"baseball","price":6},
               {"Item":"bat","price":4},
               {"Item":"book","price":12}]
expr ~ expr | Compares strings without considering case. | Get all items that start with the letter b, regardless of case.
    Mojo.Log.info("results = "+
               JSON.stringify(fjson.query("\[?Item~'b*'\]", ItemPrices)));
    results = [{"Item":"baseball","price":6},
               {"Item":"bat","price":4},
               {"Item":"book","price":12},
               {"Item":"BELL","price":10}]
..[?expression] | This will perform a deep search filter operation on all the objects and subobjects of the current data. Rather than only searching an array, this will search property values, arrays, and their children. |
$1,$2,$3, etc. | These are references to extra parameters passed to the query function or the evaluator function. |
    Mojo.Log.info("results = "+
                   JSON.stringify(fjson.query("\[?Item=$1 & price=$2\]",
                   ItemPrices,"book",12)));
    results = [{"Item":"book","price":12}]
It can also be used in an evaluator function.
    var evaluator = fjson.query("\[?Item=$1 & price=$2\]");
    Mojo.Log.info("results = "+
          JSON.stringify(evaluator(ItemPrices,"book",12)));
    results = [{"Item":"book","price":12}]
`+, -, /, *, &, |, %, (, ), <, >, <=, >=, !=` | These operators behave just as they do in JavaScript. |

Multiple operators can be used successively to create complex queries. For example, to find all the objects from the `products` array with a price less than 15, sorted in descending order by rating, and showing only the first three items from the resultant list, we could do this query:

>     //** Query = $.products[?price < 20][\rating][0:3]

>     var products = [{ "book" : "Color Purple", "price" : 21, "rating":8},
>       { "book" : "Obama", "price" : 12, "rating":4},
>       { "book" : "Pride and Prejudice", "price" : 17, "rating":6},
>       { "book" : "Winnie the Pooh", "price" : 22, "rating":9},
>       { "book" : "Crime and Punishment", "price" : 14, "rating":10},
>       { "book" : "Brothers Karamazov", "price" : 12, "rating":9}
>       { "book" : "Anna Karenina", "price" : 10}, "rating":8];

>     Mojo.Log.info("results = "+ JSON.stringify(fjson.query("\[?price < 20\]\[\\rating\]\[0:3\]", products)));
>     results = [{"book":"Crime and Punishment","price":14,"rating":10},
>       {"book":"Brothers Karamazov","price":12,"rating":9},
>       {"book":"Anna Karenina","price":10,"rating":8}]

Queries can use the regular operators to form general expressions based on more complex query operations. For example, to find the difference between the lowest priced item and the highest priced item in an array:

>     $.store.book[\price][0].price – $.store.book[/price][0].price

### Syntax

        query( {search query}, {JSON object});

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
query | Yes | string | Search query.
JSON object | No | any | JSON search object.

### Returns

>     Results of query or "compiled" function.

## Schema.validate

You can use this API to determine if a JSON data object is valid JSON. Optionally, you can also validate it against a passed JSON schema object.

### Syntax

        Schema.validate( {instance object}, {schema object});

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
instance object | Yes | any | JSON data object.
schema object | No | any | JSON schema object.

### Returns

>     {
>       valid  : boolean,
>       errors : [
>           {
>               property : string,
>               message  : string
>           }
>       ]
>     }

Argument | Required | Type | Description
--- | --- | --- | ---
valid | Yes | boolean | Is instance object valid according to schema flag.
errors | Yes | array | Array of inline objects. Returns empty array ("[]") if no errors.
errors[n].property | No | string | Property that had the error.
errors[n].message | No | string | Error message

Example

>     var jsonlib = MojoLoader.require({name:"foundations.json", version: "1.0"});
>     var fjson = jsonlib["foundations.json"];
>     var jsonSchema = {"id":"com.palm.schema.test:1",
>       "type": "object",
>       "properties" : { "_kind" : {"type": "string", "value":"com.palm.schema.test:1"},
>           "foo": {"type": "string", "description": "foo string"},
>           "bar": {"type": "string", "description": "bar string"},
>           "isMember": {"type": "boolean", "description" : "Is member flag" }
>       }
>     };

>     var BadObj = {"_kind":"com.palm.schema.test:1", "foo":"myFoo", "bar":"myBar", "isMember": "true"};

>     Mojo.Log.info("FoundJSON: BadObj validate "+ JSON.stringify(fjson.Schema.validate(BadObj, jsonSchema)));

Example Output

>     {"valid":false,"errors":[{"property":"isMember","message":"string value found, but a boolean is required"}]}

## Schema.checkPropertyChange

Checks an object property for a valid value given its schema. Unlike the validate method, it assumes the passed schema object is valid.

### Syntax

        Schema.checkPropertyChange( {instance object}, {schema object}, "property");

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
instance object | Yes | any | JSON data object.
schema object | Yes | any | JSON schema object. It is assumed this is valid.
property | Yes | string | Name of schema property.

### Returns

>     {
>       valid  : boolean,
>       errors : [
>           {
>               property : string,
>               message  : string
>           }
>       ]
>     }

Argument | Required | Type | Description
--- | --- | --- | ---
valid | Yes | boolean | Is instance object valid according to schema flag.
errors | Yes | array | Array of inline objects. Returns empty array ("[]") if no errors.
errors[n].property | No | string | Property that had the error.
errors[n].message | No | string | Error message

Example

>     var jsonlib = MojoLoader.require({name:"foundations.json", version: "1.0"});
>     var fjson = jsonlib["foundations.json"];
>     var jsonSchema = {"id":"com.palm.schema.test:1",
>       "type": "object",
>       "properties" : { "_kind" : {"type": "string", "value":"com.palm.schema.test:1"},
>           "foo": {"type": "string", "description": "foo string"},
>           "bar": {"type": "string", "description": "bar string"},
>           "isMember": {"type": "boolean", "description" : "Is member flag" }
>       }
>     };

>     var BadObj = {"_kind":"com.palm.schema.test:1", "foo":"myFoo", "bar":"myBar", "isMember": "true"};
>     var GoodObj = {"_kind":"com.palm.schema.test:1", "foo":"myFoo", "bar":"myBar", "isMember": true};
>     Mojo.Log.info("FoundJSON: checkPropertyChange good object "+ JSON.stringify(fjson.Schema.checkPropertyChange(GoodObj, jsonSchema, "isMember")));
>     Mojo.Log.info("FoundJSON: checkPropertyChange bad object "+ JSON.stringify(fjson.Schema.checkPropertyChange(BadObj, jsonSchema, "isMember")));

Example Output

>     FoundJSON: checkPropertyChange good object {"valid":true,"errors":[]},
>     FoundJSON: checkPropertyChange bad object {"valid":false,"errors":[{"property":"isMember.isMember","message":"string value found, but a boolean is required"}]}

## Transformer.initialize

Register a template containing a set of named transforms. This template is then used for all other operations.

### Syntax

        void initialize(template object);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
template object | Yes | any | JSON object containing set of named transforms.

### Returns

>     None.

Example

>     var jsonlib = MojoLoader.require({name:"foundations.json", version: "1.0"});
>     var fjson = jsonlib["foundations.json"];
>     var myTransformer = new fjson.Transformer();
>     var template =  { "user": { FirstName: "!{person.displayName}", LastName: "!{person.surname}", Country : "USA", State:"CA", Location: "!{person.building}" } };
>     myTransformer.initialize(template);

## Transformer.merge

Merge two different JSON data objects.

### Syntax

       merge (olddata, newdata);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
olddata | Yes | any | JSON data object(s) that are going to be merged with the new objects.
newdata | Yes | any | JSON data object(s) that are going to be merged with old data.

### Returns

        {
            merged data
        }

Argument | Required | Type | Description
--- | --- | --- | ---
merged data | Yes | any | JSON data object(s)—old data object(s) merged with new data object(s).

Example

>     var jsonlib = MojoLoader.require({name:"foundations.json", version: "1.0"});
>     var fjson = jsonlib["foundations.json"];
>     var myTransformer = new fjson.Transformer();
>     olddata = {"displayName": "Fred"};
>     newdata = {"street": "Main"};
>     Mojo.Log.info("FoundJSON: merge ="+ JSON.stringify(myTransformer.merge(olddata, newdata)));

Example Output

>     FoundJSON: merge ={"displayName":"Fred","street":"Main"},

## Transformer.transform

Transform JSON data object(s) according to a set of named transforms in a registered template.

A simple template might look as follows:

>     { "to": { name: "!{displayName}", type: "first" }

The `!{...}` indicates a jsonPath expression that should be evaluated for the given data.

### Syntax

        transform (data);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
data | Yes | any | JSON data object(s) that are going to be transformed given the set of named transforms in the last registered template.

### Returns

        {
            transformed data
        }

Example

>     var jsonlib = MojoLoader.require({name:"foundations.json", version: "1.0"});
>     var fjson = jsonlib["foundations.json"];
>     var myTransformer = new fjson.Transformer();
>     var template =  { "user": { FirstName: "!{person.displayName}", LastName: "!{person.surname}", Country : "USA", State:"CA", Location: "!{person.building}" } };
>     myTransformer.initialize(template);
>     var data = { "person": {"displayName" : "Fred", "surname" : "Flintstone", "building":"3-1" }};

>     Mojo.Log.info("FoundJSON: transform ="+ JSON.stringify(myTransformer.transform(data)));

Example Output

>     FoundJSON: transform ={"user":{"FirstName":"Fred","LastName":"Flintstone","Country":"USA","State":"CA","Location":"3-1"}}

## Transformer.transformAndMerge

Transform the new data with the registered template and merge it with the old data.

### Syntax

        transformAndMerge (olddata, newdata);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
olddata | Yes | any | JSON data object(s) that are going to be merged with the new objects.
newdata | Yes | any | JSON data object(s) that are going to be transformed (if template registered) then merged with old data.

### Returns

        {
            merged data
        }

Argument | Required | Type | Description
--- | --- | --- | ---
merged data | Yes | any | JSON data object(s)—old data object(s) merged with new data object(s).

Example

>     var jsonlib = MojoLoader.require({name:"foundations.json", version: "1.0"});
>     var fjson = jsonlib["foundations.json"];
>     var myTransformer = new fjson.Transformer();
>     var template = { "Address" : "!{street}"};
>     myTransformer.initialize(template);
>     olddata = {"displayName": "Fred"};
>     newdata = {"street": "Main"};
>     Mojo.Log.info("FoundJSON: transformAndmerge ="+ JSON.stringify(myTransformer.transformAndMerge(olddata, newdata)));

Example Output

>     FoundJSON: transformAndMerge ={"displayName":"Fred","Address":"Main"},