webCOMAND

cJSON

cJSON is a plain text file format used to serialize COMAND repository objects in JSON.  cJSON is identical to standard JSON, except JavaScript comments are allowed and ignored, just like JavaScript source code.  The data structures in cJSON follow a specific format described below.

JavaScript comments do not comply with standard JSON. So, to avoid confusion and respect standard JSON, COMAND JSON with comments is referred to as cJSON.

Example

/**
 * Contacts - Some contacts with photos.
 */
{
    "type": "COMAND",
    "version": "1.0",
    "title": "Contacts",
    "description": "Some contacts with photos.",
    "source": "https://comandsystems.com/contacts",
    "time": "2013-10-16T15:39:25+0400",
    "contents": [
        {
            "ID": 13,
            "OID": 2458,
            "UUID": "550e8400-e29b-41d4-a716-446655440013",
            "Type": {"UUID": "550e8400-e29b-41d4-a716-446655440123"},
            "Name": "Mary Smith",
            "EmergencyContact": {"OID": 2460},
            "PhotoFilename": "profile.jpg",
            "Photo": {"type": "url", "value": "files/1.jpg"},
            "Phones": [
                {
                    "ID": 1,
                    "OID": 2478,
                    "UUID": "550e8400-e29b-41d4-a716-446655442323",
                    "Type": {"UUID": "550e8400-e29b-41d4-a716-446655441122"},
                    "Number": "555-555-5555",
                    "Description": "Mobile"
                }
            ]
        },
        {
            "ID": 15,
            "OID": 2460,
            "UUID": "550e8400-e29b-41d4-a716-446655440015",
            "Type": {"UUID": "550e8400-e29b-41d4-a716-446655440123"},
            "Name": "John Smith",
            "EmergencyContact": {"OID": 2458}, // reference to Mary Smith
            "PhotoFilename": "mug.jpg",
            "Photo": {"type": "url", "value": "files/2.jpg"}
        }
    ]
}

cJSON is a useful format because it is open and can be easily read and written by web developers, web applications and other software to access and transfer objects, including content types, content and file data.  It is more storage efficient than XML, but less efficient than binary files and databases.

In addition to being the file system storage engine format, cJSON files can also be used for backups and to transfer objects from one repository to another.

The correct MIME Type for cJSON is "application/cjson".  If there are no comments, then the MIME Type "application/json" can be used instead.

Rules

cJSON follows the rules below.

Comments

JavaScript comments (/* enclosed */ and // end of line) are valid and ignored, the same way they are in JavaScript source code. This does not comply with standard JSON.

Top Node

The top-level node is either an object that includes metadata about the contents, or an array of those objects.  An array is used, for example, to represent the results of each query in a response to a single Get Web Service request with multiple queries.

A couple of key/value pairs are required and the rest are optional.

  • type (required) - Always the string "COMAND". This is used by the COMAND API and other applications to verify the JSON file contains COMAND objects.
  • version (required) - The version of the COMAND JSON file format. Currently this is always "1.0". Future versions should be backward compatible.
  • source - Optional URL to the COMAND Web Services that provides access to the repository that stores these objects and those they reference, or to a folder that stores this cJSON and potentially others that represent the objects it contains and/or those they reference.  Typically, filenames are composed from the top-level object UUID and ".cjson" extension
  • repository - Optional UUID of the repository (root object).  If a repository is specified and matches the destination repository UUID, then OIDs and DOIDs will be used to match existing content.  Otherwise, OID and DOID are not used to match content because the source repository is not known and they are not consistent across repositories, so it is not safe to assume they will be the same in the destination.
  • time - Optional time objects were queried, in "YYYY-MM-DDTHH:MM:SS+HH:MM" format (see ISO 8601).  While not recommended when sharing externally, any format supported by strtotime() is also supported.
  • title - Optional title of the file.
  • description - Optional summary of the contents of the file.
  • token - Optional User Token used to authenticate Web Services requests to the source.
  • contents - Array of objects.
Additional metadata is also provided in Web Service responses.  For details, see Web Services Common Response Properties.

Objects

Each object in the contents array corresponds to a Content Type defined in the repository or as a Content Type object in the contents array.  Objects contain key/value pairs, where the key corresponds to a Content Type Field Identifier, and the value that corresponds to the data, child object(s) or connection(s) for that field.  Missing fields will be populated at the destination with their default values, according to the Content Type.

Consider the following field rules.

  • _MyID - An optional ID that only has meaning within the cJSON file, used to reference this object from other objects in the same file.  It is not stored in the repository.  Objects with a _MyID, but no ID, OID, DOID or UUID, are typically new objects (see Creating New Objects).
  • OID/UUID - Just the OID or UUID key/value pair may be specified for an object. If only an OID is specified, it may reference an object in the file, or an object at the specified source.  If only a UUID is specified, it may reference an object in the file, an object at the specified source, or an object in any other repository.
  • Data Type Fields - Keys that represent a data type field may provide a value in boolean, number or string format. Text and binary values may be provided in an external file. In this case, the value will be an object with a type and value property, where the value is interpreted based on one of the following types.
    • string - value is a string that may contain JavaScript string escape codes for non-ascii characters. For example, the string "\u0227" represents the UTF8 text ȧ. This format is automatically decoded by JavaScript, but is not very space efficient (1 to 3 times the size of the raw binary data).
    • base64 - value is a base64 encoded string
    • url - value is a URL to the resource that contains the value.  The URL may reference binary or text data, and may be relative to the JSON file or URL (checked first when applicable) or source URL (checked second when defined) or absolute.
    • attachment - value is an ID that correspond to the ID provided in the response to a previous Attach Web Service request.  This is only available in the context of a Put Web Service request.
  • Content Type Single Fields - Keys that represent a content type field with a single object, whether embedded or a reference, are provided as an object.  A reference may also be a string (see References).
  • Content Type List Fields - Keys that represent a content type field with multiple objects, whether embedded or a reference, are provided as an array of objects.  If an item in the array is a reference, a string may also be provided (see References), for example "Folders": ["/Bases"].
  • Type - The Type field has special significance.  When specified, the object is created if a matching object can not be found in the target repository.  Without the Type field specified, the object fields will only be used to match the referenced object, and an error will result if it can not be found.  How objects are matched, including on unique fields, is explained in Matching Existing Objects.
  • Order Independence - Objects can be defined after other objects that reference them.
  • Approval Order - Objects are first stored in the destination repository in whatever order is determined to be necessary.  Objects are then approved in the order they appear in the file (depth-first).

Reference and Embedded Fields

When a Content Type is defined in cJSON, each Content Type Field that holds objects is either embedded (the objects are part of the parent object and are stored, copied and deleted with it) or a reference (the field points to separate objects that exist on their own).  A field is a reference when it has a Location, which is the cPath where referenced objects can be found.  Without a Location, the field is embedded.

For example, a Categories field that references separate Blog Category objects:

{
    "Type": {"UUID": "c9bcfc32-1dbd-1e11-7b76-35357616a88b"}, // Content Type Field
    "Identifier": "Categories",
    "Title": "Categories",
    "FieldType": {"UUID": "9341b796-c42e-11f1-8883-db521ca7eb6c"}, // Blog Category
    "Location": "[:cObject+]", // reference: categories can be anywhere in the repository
    "ListType": 1              // list of objects
}
If you leave out Location by mistake, the import still succeeds, but the field is created as an embedded field.  Objects assigned to it are then copied into each parent object rather than shared.

References

When a field references an object, referenced objects may be specified in one of the ways listed below, which must uniquely identify the object within the repository.  If multiple matches are found, the reference will be ignored, and typically a warning emitted.

If a reference includes a Type key/value pair, the object will be created if it can not be found, when imported.
  • Object - A JSON object with one or more of the following fields.  If multiple specified, they will be processed in the order listed below, using the first match.
    • _MyID - Refers to the _MyID of another object in the same file.
    • UUID - Refers to the Universally Unique ID of an object in the file or target repository.
    • DOID - Refers to the Dimension-specific Object ID of an object in the file or target repository.
    • OID - Refers to the Object ID of an object in the file or target repository, but only if the target repository is the same as the source/repository specified in the Top Node.
    • ID - Refers to the ID of an object in the file or target repository, but only if the target repository is the same as the source or repository specified in Top Node.
    • unique/key field - Any field in the content type that is flagged as a Unique/Key field.
  • String - A JSON string that matches one of the following (will pick first that matches)
    • cQuery - A cQuery statement, which may include cPath and/or cQL.
    • Key=Value - A unique/key field followed by an equal sign (=) and corresponding value.
    • Object Key - The cObject Key.

Object

_MyID

Refers to the _MyID of another object in the same file.  A _MyID can be a number or a string, and only needs to be unique within the file.  For a complete example, see Creating New Objects.

{"_MyID": 1}

UUID

Refers to the UUID of another object in the file or in the repository.

{"UUID": "550e8400-e29b-41d4-a716-446655440013"}

DOID

Refers to the dimension-specific object ID of another object in the file or in the repository.

{"DOID": 12345}

OID

Refers to the object ID of another object in the same cJSON file or in the repository.

{"OID": 12345}

ID

Refers to the ID of another object in the same cJSON file or in the repository.  The Type must be specified as well, if it can not be implied by the field that references it.

{"ID": 123} or {"Type": "Contact", "ID": 123}

String

cQuery

A string that contains a cQuery that resolves to a single object.

"/"

Key=Value

A key/value pair, where the key is a Field Identifier for a Unique/Key field and value is that field's value to match on.

"Identifier=Example"

Object Key

The Object Key as a string.

"Example"

Matching Existing Objects

When an object in the contents array is imported, the importer first tries to find a matching object that already exists in the repository.  If one is found, it is updated.  If not, a new object is created (when Type is specified).  Matching is attempted in the following order, and the first match is used.

  1. UUID - An object in the repository with the same UUID.
  2. DOID, OID or ID - Only when the cJSON came from the same repository (see repository in Top Node).  Otherwise these act like a _MyID, only within the file.
  3. Unique fields - If the object has no ID, OID, DOID or UUID that identifies an existing object, the values it provides for its Content Type's unique fields are used to look for an existing object of the same type.  This includes unique fields inherited from the content types it extends.  If exactly one object matches, it is updated.
  4. None - A new object is created.
Unique fields can be inherited, so an object can match an existing one even when its own Content Type defines no unique fields.  For example, any Content Type that extends Folder (including Packages and Bases) inherits the unique Title field.  Importing a Base titled "Blog" without a UUID will update an existing Base titled "Blog" if there is one, rather than create a second one.

Always Create New Objects

To import the same cJSON more than once as separate copies, unique field matching can be turned off with the PHP importer's match_unique_fields option (default true).  When it is false, objects without an ID, OID, DOID or UUID are always created, never matched.  References are still resolved as described in References.

$importer = new \io_comand_io\importer($repo);
$importer->match_unique_fields = false;
$results = $importer->import_from_file($path);
The match_unique_fields option was added in webCOMAND 4.0.  It is not available from the Content App Import panel, which always uses the default behaviour.

Creating New Objects

To add new objects, and connect them to each other, leave out ID, OID, DOID and UUID, and give each object a _MyID instead.  The importer assigns new IDs when the objects are stored, and uses the _MyID values to connect references between objects in the file.

The following example adds a folder and two contacts in it, who are each other's emergency contact.

{
    "type": "COMAND",
    "version": "1.0",
    "contents": [
        {
            "_MyID": "family",
            "Type": "Folder",
            "Title": "Family"
        },
        {
            "_MyID": "mary",
            "Type": "Contact",
            "Folders": [{"_MyID": "family"}],
            "Name": "Mary Smith",
            "EmergencyContact": {"_MyID": "john"} // defined below
        },
        {
            "_MyID": "john",
            "Type": "Contact",
            "Folders": [{"_MyID": "family"}],
            "Name": "John Smith",
            "EmergencyContact": {"_MyID": "mary"}
        }
    ]
}

Importing this file again will create two more contacts, but not another folder.  Folder Title is a unique field, so the second import finds and updates the existing "Family" folder and adds the new contacts to it.  To create a second folder as well, give it a different Title, or import with match_unique_fields turned off.

When importing from PHP, the importer returns the new UUID, DOID and OID of each imported object along with its _MyID, so code can find the objects it just created.

Processing

cJSON is processed in multiple passes to handle when objects are referenced before they are defined, cyclic relationships and other complexities.  While it often isn't necessary to understand how cJSON is processed, sometimes it is important or even critical.  For this reason, the process is outlined below.

1. Validation - The import data and references within the cJSON and potentially to existing content in the repository are validated.  If there are any errors, the import process will stop.

If the repository is not the same repository the JSON was exported from, IDs, OIDs, DOIDs are only considered "locally" within the cJSON, similar to a _MyID.  That is they can be used to establish references between content within the JSON, but will not be associated with content in the repository.  If the repository is the same as the export repository, IDs, OIDs and DOIDs will be used to reference content both within the JSON file and the repository.

2. Content Types - Any content types defined in the cJSON are applied to the repository first.  This way any content of those types that appear in the cJSON can be stored.

3. Store Objects - All objects are first stored (as a working-copy, not approved) in the repository with their field values.  If an existing object on the repository was matched, updates to that object will be stored.  Otherwise a new object will be stored.

4. Validate & Approve Objects - All stored objects are finally validated and approved in the order that they appeared in the cJSON file (depth-first).

An import is all or nothing.  If any step fails, for example because an object is not valid or a new Content Type Identifier is already in use, the errors are reported and the entire import is rolled back, so nothing is changed in the repository.

ZIP Files

cJSON and the files it references can be packaged in a single ZIP file, which is what webCOMAND creates when exporting, and what the Content App Import panel accepts.  The ZIP file must use the following layout.

contacts.zip
    contents/
        contacts.json
        files/
            1.jpg
            2.jpg
  • contents/ - All files used by the import must be inside a top-level contents folder.  Anything outside of it is ignored, so a ZIP file can also include other files, such as a readme.
  • contents/name.json - The cJSON file, with the same name as the ZIP file (for example, contacts.zip contains contents/contacts.json).
  • contents/files/ - Files referenced from the cJSON with a url value, relative to the cJSON file, such as {"type": "url", "value": "files/1.jpg"}.  A files folder is a convention, not a requirement.