Enhanced deserialisation

This is how a basic JSON input file for Tudat looks like:

main.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
{
  "initialEpoch": 0,
  "finalEpoch": 3600,
  "spice": {
    "useStandardKernels": true
  },
  "bodies": {
    "Earth": {
      "useDefaultSettings": true,
      "ephemeris": {
        "type": "constant",
        "constantState": [0, 0, 0, 0, 0, 0]
      }
    },
    "asterix": {
      "initialState": {
        "type": "keplerian",
        "semiMajorAxis": 7.5E+6,
        "eccentricity": 0.1,
        "inclination": 1.4888
      }
    }
  },
  "propagators": [
    {
      "integratedStateType": "translational",
      "bodiesToPropagate": ["asterix"],
      "centralBodies": ["Earth"],
      "accelerations": {
        "asterix": {
          "Earth": [
            {
              "type": "pointMassGravity"
            }
          ]
        }
      }
    }
  ],
  "integrator": {
    "type": "rungeKutta4",
    "stepSize": 10
  },
  "export": [
    {
      "file": "@path(epochs.txt)",
      "variables": [
        {
          "type": "independent"
        }
      ],
      "epochsInFirstColumn": false
    },
    {
      "file": "@path(states.txt)",
      "variables": [
        {
          "type": "state"
        }
      ],
      "epochsInFirstColumn": false
    }
  ],
  "options": {
    "defaultValueUsedForMissingKey": "continueSilently",
    "unusedKey": "printWarning"
  }
}

The nlohmann::json object created by parsing the contents of this file, referred to as mainJson, is used throughout the examples in this tutorial to explain the features that have been added to the JSON Interface regarding Enhanced deserialisation, Enhanced value access and Enhanced value set.

Modular files

The JSON Interface library introduces a set of functions that can be used to parse modular JSON files. A modular JSON file is a JSON file in which special syntax is used to include (parts of) other JSON files (see Modular Files). The following definitions are introduced:

  • Declaration file: file in which a JSON key and corresponding value are defined.
  • Parent file: file from which the declaration file is directly referenced.
  • Root file: file provided as input argument to the json_interface application.

The function void parseModularJSON( nlohman::json& jsonObject, const boost::filesystem::path& filePath, ... ) declared in Tudat/JsonInterface/Support/deserialization.h can be used to parse a modular JSON file. This function combines recursively the contents of all the files referenced from jsonObject obtained by parsing filePath, and updates the jsonObject (passed by reference).

Each of the referenced JSON files is parsed individually using the function nlohmann::json readJSON( const boost::filesystem::path& filePath, ... ) declared in Tudat/JsonInterface/Support/deserialization.h. In addition to parsing the contents of the file at filePath using the nlohmann::json::parse function, this function adds the following features:

  • If the file to be parsed contains a syntax error (i.e. invalid JSON syntax), the nlohmann::json::parse throws an error indicating the byte in which the syntax error is found. The readJSON catches this error and uses this information to throw an error indicating the line and column of the syntax error.
  • The file paths in the input file(s) are made relative to the root file’s directory. To do so, we have to indicate that a given string represents a relative path by using the syntax "@path(pathRelativeToDeclarationFile)" which will be replaced to "pathRelativeToRootFile".
  • The following substrings found inside any JSON string are replaced during run-time by:
    • ${FILE_DIR}: absolute path of the directory where the declaration file is located.
    • ${FILE_STEM}: filename (without extension) of the declaration file.
    • ${FILE_NAME}: filename (with extension) of the declaration file.
    • ${PARENT_FILE_DIR}: absolute path of the directory where the parent file is located.
    • ${PARENT_FILE_STEM}: filename (without extension) of the parent file.
    • ${PARENT_FILE_NAME}: filename (with extension) of the parent file.
    • ${ROOT_FILE_DIR}: absolute path of the directory where the root file is located.
    • ${ROOT_FILE_STEM}: filename (without extension) of the root file.
    • ${ROOT_FILE_NAME}: filename (with extension) of the root file.

Mergeable files

In addition to modular JSON files, in which the content of (part of) other JSON files is included, the json_interace also introduces support for mergeable JSON files (see Multi-case Simulations). Since the json_interface application expects a root JSON file containing an object, when providing a root JSON file containing an array, the contents of this array can be merged to generate a single JSON object to be used to set up the simulation. For instance, the following input file:

[
  "$(main.json)",
  {
    "bodies.asterix.initialState.eccentricity": 0
  }
]

can be merged leading to a JSON object identical to the one that would have been obtained by parsing main.json, but with the initial eccentricty of the body asterix set to 0. The file must be first de-modularized (by replacing "$(main.json)" by an object retrieved from that file) and then merged, by (re-)defining the keys indicated in the second element of the array with the corresponding values. Thus, the returned nlohmann::json has value type object. Note that the following file would not result in the same merged nlohmann::json object:

[
  "$(main.json)",
  {
    "bodies": {
      "asterix": {
        "initialState": {
          "eccentricity": 0
        }
      }
    }
  }
]

since this would re-define the key bodies of main.json to be an object containing only one element (the body asterix) whose only property would be an initialState with an eccentricity set to 0.

In order to merge a nlohmann::json of value type array into a one of value type object, the function void mergeJSON( nlohmann::json& jsonObject, const boost::filesystem::path& filePath ) declared in Tudat/JsonInterface/Support/deserialization.h is used. If the passed jsonObject is not of value type array, this function does nothing.

Full deserialisation sequence

All the features described previously are combined into the function nlohmann::json getDeserializedJSON( const boost::filesystem::path& filePath ) declared in Tudat/JsonInterface/Support/deserialization.h. This function replaces relative paths, combines modular files and merges objects when possible. This is the function that should be called when creating a nlohmann::json object to be used to set up a simulation. In general, this function is only called once during the life-cycle of the application.

When testing individual parts of the JSON Interface library, the input nlohmann::json object is not necessarily of value type object, and thus this function cannot be used, as the expected object may be of value type array. Thus, in Unit testing, modular and mergeable JSON files are not used and the function parseJSON is used instead. In practice, for non-modular files, the only thing this function does is replacing strings such as "@path(text)" by "text".

Note

Immediately before or after parsing a JSON input file (using either getDeserializedJSON or parseJSON), the current working directory must be changed to the root input file’s directory using boost::filesystem::current_path( rootFile.parent_path( ) ), so that the relative paths defined in the obtained nlohmann::json are valid.