Home > Software design >  Ansible - Object of type 'set' is not JSON serializable - when passing variable (dict) to
Ansible - Object of type 'set' is not JSON serializable - when passing variable (dict) to

Time:09-20

I have a list

  - name: List
    set_fact:
      array1: 
        - param1: "efg"
          param2: "abdc"
          param3: "nsdadsd"
          param4: "dadas dsadasda"

which looks like:

        "array1": [
            {
                "param1": "efg",
                "param2": "abdc",
                "param3": "nsdadsd",
                "param4": "dadas dsadasda"
            }
        ]

Which I want to to send in a body of the following API call

  - name: API call
    delegate_to: localhost
    uri:
      url: https://some_url/abcd/
      validate_certs: false
      method: POST
      body_format: json
      headers:
        X-some-APIKey: "{{api_token}}"
      body: 
            '{ 
              "{{array1}}"
            }'
    register: api_output

For this I get an error:

"msg": "Unable to pass options to module, they must be JSON serializable: Object of type 'set' is not JSON serializable"

I found on internet that the problem could be that it's not a string that is passed but a "set" and one of the answers was to convert it to a string, so I tried this

  - name: array2 
    set_fact:
      array2: "{{array1 | string }}"

Also tried different variations with quotes, either ' or " for the values and even parameters, but nothing helped.

Update:

Let me add that just for the test I changed the array to be just with one item, and still getting "serializable" error. So I'm thinking maybe it's not the array which is a problem, but syntax for calling this variable from an API?

  - name: array
    set_fact:
      array1: 
        - abdcdef

SOLUTION: Update no2.

Problem was with two things. First there was Content-Type: "application/json" missing from API call. Second was that this API web service does not accept arrays.

CodePudding user response:

You are creating an invalid json, in your example you're trying to create a structure similar to

{
  [
    {
      "param1": "efg",
      "param2": "abdc",
      "param3": "nsdadsd",
      "param4": "dadas dsadasda"
     }
   ]
}

which is invalid, since {} implies name/value pair, see https://www.json.org/json-en.html

Ansible has a to_json filter, which helps converting vars to a valid json, so creating a correct fact and then converting it to json sounds like a good idea.

If your api accepts arrays, the following playbook should work:

- hosts: localhost
  tasks:
    - name: List
      set_fact:
        list1:
          - param1: "efg"
            param2: "abdc"
            param3: "nsdadsd"
            param4: "dadas dsadasda"

    - name: API call
      delegate_to: localhost
      uri:
        url: http://localhost:8080
        method: POST
        body_format: json
        body: "{{list1 | to_json }}"
      register: api_output

Tested with awesome https://hub.docker.com/r/mendhak/http-https-echo image, which echoes http requests, using podman run -p 8080:8080 --rm -t mendhak/http-https-echo:24, output contains:

{
    "path": "/",
    "headers": {
        "accept-encoding": "identity",
        "content-length": "86",
        "host": "localhost:8080",
        "user-agent": "ansible-httpget",
        "content-type": "application/json",
        "connection": "close"
    },
    "method": "POST",
    "body": "[{\"param1\": \"efg\", \"param2\": \"abdc\", \"param3\": \"nsdadsd\", \"param4\": \"dadas dsadasda\"}]",
    "fresh": false,
    "hostname": "localhost",
    "ip": "::ffff:10.0.2.100",
    "ips": [],
    "protocol": "http",
    "query": {},
    "subdomains": [],
    "xhr": false,
    "os": {
        "hostname": "40f1fcddf57d"
    },
    "connection": {},
    "json": [
        {
            "param1": "efg",
            "param2": "abdc",
            "param3": "nsdadsd",
            "param4": "dadas dsadasda"
        }
    ]
}

Update:

With info from comments, it seems that array is not required, then fact setting task should be converted to

    - name: List
      set_fact:
        list1:
          param1: "efg"
          param2: "abdc"
          param3: "nsdadsd"
          param4: "dadas dsadasda"

however, to not to confuse futher readers of your code, list should be renamed to dict in both task name and fact name

  • Related