Ansible:当字典值为字典列表时识别对应Key
问题背景与需求
现有如下结构的Ansible变量字典:
grouped: 10000: - {Id: 10001, Name: North_America, Parent_Id: 10000, Type: Country} 10001: - {Id: 10011, Name: Maine, Parent_Id: 10001, Type: State} - {Id: 10012, Name: Colorado, Parent_Id: 10001, Type: State} - {Id: 10013, Name: Texas, Parent_Id: 10001, Type: State} 10011: - {Id: 10101, Name: Augusta, Parent_Id: 10011, Type: City} - {Id: 10102, Name: Portland, Parent_Id: 10011, Type: City} 10012: - {Id: 10103, Name: Denver, Parent_Id: 10012, Type: City} 10013: - {Id: 10104, Name: Austin, Parent_Id: 10013, Type: City} - {Id: 10105, Name: Houston, Parent_Id: 10013, Type: City} 10101: - {Id: 11001, Name: First_st, Parent_Id: 10101, Type: Street} - {Id: 11002, Name: Second_st, Parent_Id: 10101, Type: Street} 10102: - {Id: 11003, Name: First_st, Parent_Id: 10102, Type: Street} - {Id: 11004, Name: Second_st, Parent_Id: 10102, Type: Street} 10104: - {Id: 11005, Name: First_st, Parent_Id: 10104, Type: Street} - {Id: 11006, Name: Second_st, Parent_Id: 10104, Type: Street}
需要实现的功能:
给定类似North_America-Texas-Austin-First_st的层级字符串,按Country-State-City-Street顺序完成以下操作:
- 逐级查找对应名称的项,获取其ID作为下一级的查询键
- 若某层级的名称不存在,则将其添加到字典中
当前在第一步查找North_America的ID时遇到问题,尝试的代码及错误如下:
尝试的代码
--- hosts: localhost gather_facts: false connection: local vars: target: "North_America-Colorado-Denver-First_st" tasks: - name: Get the id using json filter and jmespath query set_fact: group_id: "{{ grouped | json_query(ID_query) | map(attribute='key') }}" vars: ID_query: '[?[*].Name == `North-America`]' - debug: var=group_id - name: Get the id using selectattr set_fact: group_id2: "{{ grouped | dict2items | selectattr('Name', 'search', 'North_America') }}" - debug: var=group_id2
错误输出
第一个任务的debug结果为空列表:
ok: [localhost] => { "group_id": [] }
第二个任务报错,提示字典对象不存在Name属性:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'Name'..."}
解决方案
1. 错误原因分析
- json_query问题:原查询存在两处错误:一是拼写错误(目标名称为
North_America而非North-America);二是未正确遍历字典键值对,需先将字典转为键值对列表,再筛选值中包含目标Name的项。 - selectattr问题:
dict2items转换后,每个元素是{key: "...", value: [...]}结构,没有直接的Name属性,需先展开value列表,再筛选其中的Name字段。
2. 正确的逐级查找实现
以下是完整的任务示例,先拆分目标字符串,再逐级查找ID:
--- hosts: localhost gather_facts: false connection: local vars: target: "North_America-Colorado-Denver-First_st" target_levels: "{{ target.split('-') }}" current_parent_id: "10000" # 根节点ID tasks: - name: 遍历层级查找对应ID set_fact: current_parent_id: >- {% set found = grouped[current_parent_id] | selectattr('Name', 'equalto', item) | first | default(none) %} {% if found is not none %} {{ found.Id | string }} {% else %} {{ current_parent_id }} {% endif %} loop: "{{ target_levels }}" register: level_results - name: 获取最终查找结果 set_fact: final_id: "{{ level_results.results | last | json_query('ansible_facts.current_parent_id') }}" - name: 检查所有层级是否存在 set_fact: all_exists: >- {% set exists = true %} {% for res in level_results.results %} {% if res.ansible_facts.current_parent_id == current_parent_id %} {% set exists = false %} {% endif %} {% endfor %} {{ exists }} - debug: msg: "目标层级存在,最终ID: {{ final_id }}" when: all_exists - debug: msg: "目标层级不存在,需要添加" when: not all_exists
3. 缺失层级的添加逻辑(示例)
若需添加不存在的层级,可在遍历过程中记录缺失项,生成新ID后添加到字典:
- name: 记录缺失的层级及父ID set_fact: missing_levels: "{{ missing_levels | default([]) + [{ 'parent_id': current_parent_id, 'name': item }] }}" loop: "{{ target_levels }}" when: >- grouped[current_parent_id] | selectattr('Name', 'equalto', item) | list | length == 0 - name: 添加缺失层级到grouped字典 set_fact: grouped: >- {% set new_id = (grouped | dict2items | map(attribute='key') | map('int') | max + 1) | string %} {% set new_item = [{ 'Id': new_id, 'Name': item.name, 'Parent_Id': item.parent_id, 'Type': level_type }] %} {% set updated_grouped = grouped | combine({ item.parent_id: grouped[item.parent_id] + new_item }, recursive=true) %} {{ updated_grouped | combine({ new_id: [] }) }} loop: "{{ missing_levels }}" vars: level_index: "{{ loop.index0 }}" level_type: >- {{ 'Country' if level_index == 0 else 'State' if level_index == 1 else 'City' if level_index == 2 else 'Street' }}
内容的提问来源于stack exchange,提问作者piercjs
相关产品推荐
相关产品推荐

