How to parse specific variable of a dict in Ansible - json

I have an Ansible job that run on 2 or more urls. Each url returns the same variables with different values (but have similar pattern). Here is the return data of the job:
"message": [
{
"url": "http://0.0.0.1:xxx1",
"content": [
{
"message": "This is message number 1",
"message2": "This is message2 number 1"
},
{
"message": "This is message number 2",
"message2": "This is message2 number 2"
},
{
"message": "This is message number 3",
"message2": "This is message2 number 3"
}
]
},
{
"url": "http://0.0.0.2:xxx2",
"content": [
{
"message": "This is message number 1",
"message2": "This is message2 number 1"
},
{
"message": "This is message number 2",
"message2": "This is message2 number 2"
},
{
"message": "This is message number 3",
"message2": "This is message2 number 3"
}
]
}
I want to parse the variable 'message' without changing the structure of the returned data. My expected result is like this:
"message": [
{
"url": "http://0.0.0.1:xxx1",
"content": [
{
"message": "message number 1",
"message2": "This is message2 number 1"
},
{
"message": "message number 2",
"message2": "This is message2 number 2"
},
{
"message": "message number 3",
"message2": "This is message2 number 3"
}
]
},
{
"url": "http://0.0.0.2:xxx2",
"content": [
{
"message": "message number 1",
"message2": "This is message2 number 1"
},
{
"message": "message number 2",
"message2": "This is message2 number 2"
},
{
"message": "message number 3",
"message2": "This is message2 number 3"
}
]
}
I know how to parse the data. My struggle is to keep the structure as is. How can I achieve what I want?

Convert the structure in Jinja
updates: |
{% for i in message %}
- content:
{% for m in i.content %}
{% set arr=m.message.split() %}
{% set message={'message': arr[2:]|join(' ')} %}
- {{ m|combine(message) }}
{% endfor %}
{% for k in i %}
{% if k != 'content' %}
{{ k }}: {{ i[k] }}
{% endif %}
{% endfor %}
{% endfor %}
gives
updates: |-
- content:
- {'message': 'message number 1', 'message2': 'This is message2 number 1'}
- {'message': 'message number 2', 'message2': 'This is message2 number 2'}
- {'message': 'message number 3', 'message2': 'This is message2 number 3'}
url: http://0.0.0.1:xxx1
- content:
- {'message': 'message number 1', 'message2': 'This is message2 number 1'}
- {'message': 'message number 2', 'message2': 'This is message2 number 2'}
- {'message': 'message number 3', 'message2': 'This is message2 number 3'}
url: http://0.0.0.2:xxx2
Convert the block to YAML
messag2: "{{ updates|from_yaml }}"
gives
messag2:
- content:
- message: message number 1
message2: This is message2 number 1
- message: message number 2
message2: This is message2 number 2
- message: message number 3
message2: This is message2 number 3
url: http://0.0.0.1:xxx1
- content:
- message: message number 1
message2: This is message2 number 1
- message: message number 2
message2: This is message2 number 2
- message: message number 3
message2: This is message2 number 3
url: http://0.0.0.2:xxx2
Example of a complete playbook for testing
- hosts: localhost
vars:
message:
- content:
- message: This is message number 1
message2: This is message2 number 1
- message: This is message number 2
message2: This is message2 number 2
- message: This is message number 3
message2: This is message2 number 3
url: http://0.0.0.1:xxx1
- content:
- message: This is message number 1
message2: This is message2 number 1
- message: This is message number 2
message2: This is message2 number 2
- message: This is message number 3
message2: This is message2 number 3
url: http://0.0.0.2:xxx2
updates: |
{% for i in message %}
- content:
{% for m in i.content %}
{% set arr=m.message.split() %}
{% set message={'message': arr[2:]|join(' ')} %}
- {{ m|combine(message) }}
{% endfor %}
{% for k in i %}
{% if k != 'content' %}
{{ k }}: {{ i[k] }}
{% endif %}
{% endfor %}
{% endfor %}
messag2: "{{ updates|from_yaml }}"
tasks:
- debug:
var: updates
- debug:
var: messag2
Q: "This solution works in vars. Using it in tasks, since my data is from json output, set_fact gives me an offending line in the result."
A: There is no reason for moving the updates and messag2 variables into the set_fact task.
a) The play below works fine also when message comes from a task
- hosts: localhost
vars:
updates: |
{% for i in message %}
- content:
{% for m in i.content %}
{% set arr=m.message.split() %}
{% set message={'message': arr[2:]|join(' ')} %}
- {{ m|combine(message) }}
{% endfor %}
{% for k in i %}
{% if k != 'content' %}
{{ k }}: {{ i[k] }}
{% endif %}
{% endfor %}
{% endfor %}
messag2: "{{ updates|from_yaml }}"
tasks:
- include_vars:
file: message.yml
- debug:
var: updates
- debug:
var: messag2
b) You can put the declarations of updates and messag2 into the set_fact tasks. The play below works fine
- hosts: localhost
tasks:
- include_vars:
file: message.yml
- set_fact:
updates: |
{% for i in message %}
- content:
{% for m in i.content %}
{% set arr=m.message.split() %}
{% set message={'message': arr[2:]|join(' ')} %}
- {{ m|combine(message) }}
{% endfor %}
{% for k in i %}
{% if k != 'content' %}
{{ k }}: {{ i[k] }}
{% endif %}
{% endfor %}
{% endfor %}
- set_fact:
messag2: "{{ updates|from_yaml }}"
- debug:
var: updates
- debug:
var: messag2
c) You can't put both declarations of updates and messag2 into single set_fact task because messag2 knows nothing about updates this way. The play below
- hosts: localhost
tasks:
- include_vars:
file: message.yml
- set_fact:
updates: |
{% for i in message %}
- content:
{% for m in i.content %}
{% set arr=m.message.split() %}
{% set message={'message': arr[2:]|join(' ')} %}
- {{ m|combine(message) }}
{% endfor %}
{% for k in i %}
{% if k != 'content' %}
{{ k }}: {{ i[k] }}
{% endif %}
{% endfor %}
{% endfor %}
messag2: "{{ updates|from_yaml }}"
- debug:
var: updates
- debug:
var: messag2
failes
... The error was: 'updates' is undefined ...

Related

How can I fetch a value from a JSON dictionary?

I am trying to grab the value green from the below JSON data dictionary.
The API endpoint located at http://localhost:9200/api/status give the below data:
{
"name":"prod01",
"uuid":"3430c40-e786-4325-bc48-e0a096956000",
"version":{
"number":"7.17.0",
"build_hash":"60a9838d21b6420bbdb5a4d07099111b74c68ceb",
"build_number":46534,
"build_snapshot":false
},
"status":{
"overall":{
"since":"2023-02-13T22:47:05.381Z",
"state":"green",
"title":"Green",
"nickname":"Looking good",
"icon":"success",
"uiColor":"secondary"
},
"statuses":[
{
"id":"core:elasticsearch#7.17.0",
"message":"Elasticsearch is available",
"since":"2023-02-13T22:47:05.381Z",
"state":"green",
"icon":"success",
"uiColor":"secondary"
},
{
"id":"core:savedObjects#7.17.0",
"message":"SavedObjects service has completed migrations and is available",
"since":"2023-02-13T22:47:05.381Z",
"state":"green",
"icon":"success",
"uiColor":"secondary"
}
]
}
}
And the test.sls file doing the job is:
{% set json_data = salt.cp.get_url('http://localhost:9200/api/status', dest=None) | load_json %}
{% for key, value in json_data.items() %}
{% if value['state'] == 'green' %}
{{ key }}: {{ value }} (found)
{% else %}
{{ key }}: {{ value }}
{% endif %}
{% endfor %}
Executing it, I get the error:
Rendering SLS 'base:svr.salt.dev.test' failed: Jinja variable 'str object' has no attribute 'state'
You are looping on all the key-value pairs of the object json_data, with json_data.items(), so, you do not have a my_dictionary['state'] anymore, what you have is a key which will be state and its value will be green.
This said, your {"state": "green"} is not in the root of your dictionary, so you will never find any key-value pair matching what you need.
What you can do, though is:
{% if load_json.status.overall.state == 'green' -%}
state: {{ load_json.status.overall.nickname }}
{% endif %}
Which would yield:
state: Looking good

JSON parse error on line 5: expecting 'STRING', ' , got "undefined'

I keep getting an error on line 5 and am not sure why, my quotation marks seem good.
{
"tickets": [
{
"id": "{{ticket.id}}",
"additional_tags": [{% for cc in ticket.ccs %}"{{ cc.email }}"{% if forloop.last == false %},{% endif %}{% endfor %}]
}
]
}

How can I access JSON identifiers from Twig?

These are my JSON data:
[
{
"name":"unit 1",
"lessons":{
"1":{
"name":"lesson 1",
"content":"......."
},
"2":{
"name" "lesson 2",
"content":"......."
}
}
]
I can access these data using the following Twig code:
{% for unit in units %}
{{ unit.name }}
{% for lesson in unit.lessons %}
{{ lesson.name }}
{% endfor %}
{% endfor %}
But I want to access the lesson number, to make a hyperlink to the content. How can I do that?
You can Iterating over Keys and values as example:
{% for key, lesson in unit.lessons %}
{{ key}}
{{ lesson.name }}
{% endfor %}
Hope this help

How to limit number of iterations when looping through object in nunjucks

I have a js object like this:
var data = [
{ src: "src1", name: "name 1" },
{ src: "src2", name: "name 2" },
{ src: "src3", name: "name 3" }
]
I am looping through it with Nunjucks:
{% for object in data %}
{{object.src}}
{% endfor %}
But I want to limit the number of iterations to 2.
How do I do that with Nunjucks?
I know there is a range option but I couldn't find how to use it in this case.
You could accomplish this a couple different ways:
A) Use loop.index0 special variable (Demo in Codepen)
Inside a for loop, you can use loop.index0 instead limit-var
{% for obj in data %}
{% if loop.index0 < 2 %}
{{obj.src}}: {{obj.name}}
{% endif %}
{% endfor %}
B) Add Custom Filter (Demo in Codepen)
But more readable is add custom filter limit and use it
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
var data = [
{src: "src1", name: "name 1"},
{src: "src2", name: "name 2"},
{src: "src3", name: "name 3"}
];
env.addFilter('limit', function(arr, limit) {
return arr.slice(0, limit);
});
var res = nunjucks.renderString(`
{% for obj in data | limit(2) %}
{{obj.src}}: {{obj.name}}
{% endfor %}`,
{data}
);
console.log(res);
C) Use native slice() function (Demo in Codepen)
{% for obj in data.slice(0, 2) %}
{{obj.src}}: {{obj.name}}
{% endfor %}

django not able to access jsonfield data in templates

Actually, I don't know how to access jsonfield data in the template. I tried something like this
{% for key in place.places_to_visit %}
{{ key }}:{{ value }}
{% endfor %}
But it is showing all json data that I have added including {} and " " and also same for Arrayfield of characters
I also want to add an image in that jsonfield, how to do that? Does that image need to be media directory and then I should include its path? Or some other ways to do that?
It is also showing error as follows
"[05/Sep/2017 23:03:00] "GET /ontheway/1/ HTTP/1.1" 200 4238"
jsonfield data of places_to visit is
"Places to visit": [
{
"name" : "some name",
"overview" : " some data",
"iamge": "image path"
}
{
"name" : "some name",
"Overview": " some data",
"image": "image path"
}
]
Thank you in advance
you can try to use items:
{% for data in place.places_to_visit %}
{% for key, value in data.items %}
{% if key == 'image' %}
<img src="{{ value }}">
{% else %}
<div> {{ key }}:{{ value }} </div>
{% endif %}
{% endfor %}
{% endfor %}