How to Access Data in a Property Tree

| Home | Libraries | People | FAQ | More

## How to Access Data in a Property Tree

Property tree resembles (almost is) a standard container with value type of `pair<string, ptree>`. It has the usual member functions, such as `insert`, `push_back`, `find`, `erase`, etc. These can of course be used to populate and access the tree. For example the following code adds key `"pi"` with data (almost) equal to mathematical pi value:

    ptree pt;
    pt.push_back(ptree::value_type("pi", ptree("3.14159")));

To find the value of `pi` we might do the following:

    ptree::const_iterator it = pt.find("pi");
    double pi = boost::lexical_cast<double>(it->second.data());

This looks quite cumbersome, and would be even more so if `pi` value was not stored so near the top of the tree, and we cared just a little bit more about errors. Fortunately, there is another, correct way of doing it:

    ptree pt;
    pt.put("pi", 3.14159);                // put double
    double pi = pt.get<double>("pi");     // get double

It doesn't get simpler than that. Basically, there are 2 families of member functions, `get` and `put`, which allow intuitive access to data stored in the tree (direct children or not).

#### Three Ways of Getting Data

There are three versions of get: get, get (default-value version), and get_optional, which differ by failure handling strategy. All versions take path specifier, which determines in which key to search for a value. It can be a single key, or a path to key, where path elements are separated with a special character (a '.' if not specified differently). For example debug.logging.errorlevel might be a valid path with dot as a separator.

1. The throwing version (`get`):

       ptree pt;
       /* ... */
       float v = pt.get<float>("a.path.to.float.value");

   This call locates the proper node in the tree and tries to translate its data string to a float value. If that fails, exception is thrown. If path does not exist, it will be `ptree_bad_path` exception. If value could not be translated, it will be `ptree_bad_data`. Both of them derive from `ptree_error` to make common handling possible.

2. The default-value version (`get`):

       ptree pt;
       /* ... */
       float v = pt.get("a.path.to.float.value", -1.f);

   It will do the same as above, but if it fails, it will return the default value specified by second parameter (here -1.f) instead of throwing. This is very useful in common situations where one wants to allow omitting of some keys. Note that type specification needed in throwing version is normally not necessary here, because type is determined by the default value parameter.

3. The optional version (`get_optional`):

       ptree pt;
       /* ... */
       boost::optional<float> v = pt.get_optional<float>("a.path.to.float.value");

   This version uses boost::optional class to handle extraction failure. On successful extraction, it will return boost::optional initialized with extracted value. Otherwise, it will return uninitialized boost::optional.

To retrieve a value from this tree (not some subkey), use `get_value`, `get_value` (default-value version), and `get_value_optional`. They have identical semantics to `get` functions, except they don't take the `path` parameter. Don't call `get` with and empty `path` to do this as it will try to extract contents of subkey with empty name.

To use a separator character other than default '`.`', you need to construct a path object explicitly. The path type for a `ptree` is a string_path instantiation, so the easiest way to refer to it is `ptree`::path_type. This way you can use trees that have dots in their keys:

    typedef ptree::path_type path;
    pt.get<float>(path("p.a.t.h/t.o/v.a.l.u.e", '/'));
    pt.get(path("p.a.t.h/t.o/v.a.l.u.e", '/'), 0, NULL);
    pt.get_optional<std::string>(path("p.a.t.h/t.o/v.a.l.u.e", '/'));

Note: the special overloads of `get` and `get_optional` taking a separator character that existed in pre-release versions of PropertyTree have been removed. This is because the overloads conflicted with using per-call data translators.

#### Two Ways of Putting Data

To complement `get`, there are `put` and `add`. Contrary to `get`, they have only one variant each. This is because there is no need to deal with missing values when adding data. If the supplied value cannot be converted to the tree's data type, the functions will throw `ptree_bad_data`.

    ptree pt;
    pt.put("a.path.to.float.value", 3.14f);
    // Overwrites the value
    pt.put("a.path.to.float.value", 2.72f);
    // Adds a second node with the new value.
    pt.add("a.path.to.float.value", 3.14f);

Calling `put` will insert a new value at specified path, so that a call to `get` specifying the same path will retrieve it. Further, `put` will insert any missing path elements during path traversal. For example, calling `put("key1.key2.key3", 3.14f)` on an empty tree will insert three new children: `key1`, `key1.key2` and `key1.key2.key3`. The last one will receive a string `"3.14"` as data, while the two former ones will have empty data strings. `put` always inserts new keys at the back of the existing sequences. The difference between `put` and `add` is that put will overwrite existing values if there are any, while add will create a new node to hold the value even if the specified path references an existing node.

Similar to `get_value`, there is also a `put_value` function. It does the same for this property tree what `put` does for its children. Thus, it does not receive a `path`:

    ptree pt;
    pt.put_value(3.14f);

There is no add_value function.