Ansible set_fact任务报类型错误:期望字符串却得到列表,如何解决?
问题:Ansible Playbook处理userdata字典列表时的类型错误解决
需求与现有代码
需要对userdata(由字典组成的列表)进行处理,去除所有键和值的首尾空格,得到处理后的字典列表用于后续生成CSV。现有Playbook代码如下:
- hosts: localhost tasks: - set_fact: userdata: | {% filter from_yaml %} {% for i in userdata %} {% set keys=i.keys()|map('trim') %} {% set values=i.values()|map('trim') %} - {{ dict(keys|zip(values)) }} {% endfor %} {% endfilter %}
错误信息
执行时出现类型错误:
{"msg": "Unexpected templating type error occurred on ({% filter from_yaml %} {% for i in userdata %} {% set keys=i.keys()|map('trim') %} {% set vals=i.values()|map('trim') %} - {{ dict(keys|zip(vals)) }} {% endfor %} {% endfilter %} ): sequence item 0: expected str instance, list found"}
移除{% filter from_yaml %}后,userdata会变成YAML格式的字符串,不符合后续处理需求。
原始userdata结构
{ "userdata": [ { "ClassType": "Part Time", "Code": " #3091 ", "DateofSubmission": " 14/5/2023 ", "Description": "NIL", "FirstName": " Peter ", "LastName": " Miller ", "path": "OU=fulltime,OU=Test OU,DC=localdemo,DC=local" }, { "ClassType": "Full Time", "Code": " #3092 ", "DateofSubmission": " 2/5/2023 ", "Description": "NIL", "FirstName": " Sam ", "LastName": " Keller ", "path": "OU=parttime,OU=Test OU,DC=localdemo,DC=local" }, { "ClassType": "Flexi", "Code": " #3093 ", "DateofSubmission": " 10/5/2023 ", "Description": "NIL", "FirstName": " Judy ", "LastName": " Higgins ", "path": "OU=flexi,OU=Test OU,DC=localdemo,DC=local" } ] }
解决方案
错误原因
原有写法错误地将Python字典对象直接渲染为字符串,再通过from_yaml解析。但dict(keys|zip(values))输出的是Python字典的字符串表示(如{'key': 'val'}),而非标准YAML映射格式(如key: val),导致YAML解析时出现类型不匹配。
优化后的Playbook
直接通过Ansible过滤器链生成处理后的字典列表,无需经过YAML字符串转换:
- hosts: localhost tasks: - set_fact: userdata: "{{ userdata | map('items') | map('map', 'trim') | map('dict') | list }}"
过滤器链说明
map('items'):将每个字典转换为(key, value)元组列表map('map', 'trim'):对每个元组中的键和值都执行首尾空格去除map('dict'):将处理后的元组列表重新转换为字典list:将迭代器转换为最终的列表结构
备选写法(显式循环)
如果需要更直观的逻辑,也可以用显式循环构建处理后的列表:
- hosts: localhost tasks: - set_fact: userdata: >- {% set processed_list = [] %} {% for item in userdata %} {% set trimmed_dict = {} %} {% for k, v in item.items() %} {% set _ = trimmed_dict.update({k|trim: v|trim}) %} {% endfor %} {% set _ = processed_list.append(trimmed_dict) %} {% endfor %} {{ processed_list }}
内容的提问来源于stack exchange,提问作者Blitzden
相关产品推荐
相关产品推荐

