mapdata

Table of Contents

Prototype: mapdata(interpretation, pattern, array_or_container)

Return type: data

Description: Returns a data container holding a JSON array. The array is a map across each element of array_or_container, modified by a pattern. The map is either collected literally when interpretation is none, or canonified when interpretation is canonify, or parsed as JSON when interpretation is json, or collected from pattern, invoked as a program, when interpretation is json_pipe.

This function can accept many types of data parameters.

This function can delay the evaluation of its second parameter, which can therefore be a function call.

The $(this.k) and $(this.v) variables expand to the key and value of the current element, similar to the way this is available for maplist.

If the array or data container has two levels, you'll also be able to use the $(this.k[1]) variable for the key at the second level. See the example below for an illustration.

The order of the keys is not guaranteed. Use the sort() function if you need order in the resulting output.

Arguments:

  • interpretation: - Conversion to apply to the mapped string - one of
    • none
    • canonify
    • json
    • json_pipe
  • pattern: string - Pattern based on $(this.k) and $(this.v) as original text - in the range: .*
  • array_or_container: string - CFEngine variable identifier or inline JSON - in the range: .*

Example:

body common control
{
      bundlesequence => { "run" };
}

bundle agent run
{
  vars:
      "myarray[lookup][big]" string => "lookup big";
      "myarray[lookup][small]" string => "lookup small";

      # every item must parse as valid JSON when the interpretation is `json`
      "mapa_json" data => mapdata("json", '{ "key": "$(this.k)", "key2": "$(this.k[1])", "value": "$(this.v)" }', myarray);
      "mapa_json_str" string => format("%S", mapa_json);

      # every item is just a string when the interpretation is `none`
      "mapa_none" data => mapdata("none", 'key=$(this.k), level 2 key = $(this.k[1]), value=$(this.v)', myarray);
      "mapa_none_str" string => format("%S", mapa_none);

      "mycontainer" data => parsejson('
{
  "top":
  {
    "x": 100,
    "y": 200
  }
}');

      # every item must parse as valid JSON when the interpretation is `json`
      "mapc_json" data => mapdata("json", '{ "key": "$(this.k)", "key2": "$(this.k[1])", "value": "$(this.v)" }', mycontainer);
      "mapc_json_str" string => format("%S", mapc_json);

      # every item is just a string when the interpretation is `none`
      "mapc_none" data => mapdata("none", 'key=$(this.k), level 2 key = $(this.k[1]), value=$(this.v)', mycontainer);
      "mapc_none_str" string => format("%S", mapc_none);

  reports:
    show_example::
      "mapdata/json on classic CFEngine array result: $(mapa_json_str)";
      "mapdata/none on classic CFEngine array result: $(mapa_none_str)";
      "mapdata/json on data container result: $(mapc_json_str)";
      "mapdata/none on data container result: $(mapc_none_str)";

    any::
      "Note that the output of the above reports is not deterministic,";
      "because the order of the keys returned by mapdata() is not guaranteed.";
}

Output: (when show_example is defined)

R: mapdata/json on classic CFEngine array result: [{"key":"lookup","key2":"big","value":"lookup big"},{"key":"lookup","key2":"small","value":"lookup small"}]
R: mapdata/none on classic CFEngine array result: ["key=lookup, level 2 key = big, value=lookup big","key=lookup, level 2 key = small, value=lookup small"]
R: mapdata/json on data container result: [{"key":"top","key2":"x","value":"100"},{"key":"top","key2":"y","value":"200"}]
R: mapdata/none on data container result: ["key=top, level 2 key = x, value=100","key=top, level 2 key = y, value=200"]

json_pipe

The json_pipe interpretation is intended to work with programs that take JSON as input and produce JSON as output. This is a standard tool convention in the Unix world. See the example below for the typical usage.

jq has a powerful programming language that fits the json_pipe interpretation well. It will take JSON input and product JSON output. Please read the jq manual and cookbook to get a feel for the power of this tool. When available, jq will offer tremendous data manipulation power for advanced cases where the built-in CFEngine functions are not enough.

Example with json_pipe:

body common control
{
      bundlesequence => { "run" };
}

bundle agent run
{
  vars:
      "tester" data => '{ "x": 100, "y": [ true, "a", "b" ] }';

      # "jq ." returns the same thing that was passed in
      "pipe_passthrough" data => mapdata("json_pipe", '$(def.jq) .', tester);
      "pipe_passthrough_str" string => format("%S", pipe_passthrough);

      # "jq .x" returns what was under x wrapped in an array: [100]
      "pipe_justx" data => mapdata("json_pipe", '$(def.jq) .x', tester);
      "pipe_justx_str" string => format("%S", pipe_justx);

      # "jq .y" returns what was under y wrapped in an array: [[true,"a","b"]]
      "pipe_justy" data => mapdata("json_pipe", '$(def.jq) .y', tester);
      "pipe_justy_str" string => format("%S", pipe_justy);

      # "jq .y[]" returns each entry under y *separately*: [true,"a","b"]
      "pipe_yarray" data => mapdata("json_pipe", '$(def.jq) .y[]', tester);
      "pipe_yarray_str" string => format("%S", pipe_yarray);

      # "jq .z" returns null because the key "z" is missing: [null]
      "pipe_justz" data => mapdata("json_pipe", '$(def.jq) .z', tester);
      "pipe_justz_str" string => format("%S", pipe_justz);

      # "jq" can do math too! and much more!
      "pipe_jqmath" data => mapdata("json_pipe", '$(def.jq) 1+2+3', tester);
      "pipe_jqmath_str" string => format("%S", pipe_jqmath);

  reports:
      "mapdata/json_pipe passthrough result: $(pipe_passthrough_str)";
      "mapdata/json_pipe just x result: $(pipe_justx_str)";
      "mapdata/json_pipe just y result: $(pipe_justy_str)";
      "mapdata/json_pipe array under y result: $(pipe_yarray_str)";
      "mapdata/json_pipe just z result: $(pipe_justz_str)";
      "mapdata/json_pipe math expression result: $(pipe_jqmath_str)";
}

Output:

R: mapdata/json_pipe passthrough result: [{"x":100,"y":[true,"a","b"]}]
R: mapdata/json_pipe just x result: [100]
R: mapdata/json_pipe just y result: [[true,"a","b"]]
R: mapdata/json_pipe array under y result: [true,"a","b"]
R: mapdata/json_pipe just z result: [null]
R: mapdata/json_pipe math expression result: [6]

History: Was introduced in 3.7.0. canonify mode was introduced in 3.9.0. The collecting function behavior was added in 3.9. The json_pipe mode was added in 3.9. The delayed evaluation behavior was introduced in 3.10.

See also: maplist(), maparray(), canonify(), about collecting functions, and data documentation.