Welcome to support for indentation with spaces in to_xml plugin (#192)
* Welcome to support for indentation with spaces in to_xml plugin * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changelog fragment * Unit tests for: * Indent with spaces and explicit indent_width (4) * Invalid indent I also slightly modified VALID_DATA to produce output with indentation, so existing test_valid_data and OUTPUT has been updated accordingly. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix changelog section name to minor_changes Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>pull/222/head
parent
2725ed3056
commit
0aee02eb97
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
minor_changes:
|
||||
- >-
|
||||
to_xml - Added support for using spaces to indent an XML doc via a new
|
||||
`indent` parameter.
|
|
@ -73,6 +73,47 @@ Parameters
|
|||
<div>Conversion library to use within the filter plugin.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>indent</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>tabs</b> ←</div></li>
|
||||
<li>spaces</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>The character used for indentation (defaults to tabs).</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>indent_width</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">integer</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">4</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>The number of spaces to use to indent output data.</div>
|
||||
<div>This option is only used when indent="spaces", otherwise it is ignored.</div>
|
||||
<div>When indent="tabs", a single tab is always used for indentation.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
|
@ -149,6 +190,38 @@ Examples
|
|||
# Cisco-IOS-XR-ifmgr-cfg\">\n\t<interface-configuration></interface-configuration>\n</interface-configurations>"
|
||||
# }
|
||||
|
||||
#### example3 with indent='spaces' and indent_width=2
|
||||
|
||||
- name: Define JSON data
|
||||
ansible.builtin.set_fact:
|
||||
data:
|
||||
"interface-configurations":
|
||||
"@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"
|
||||
"interface-configuration":
|
||||
- debug:
|
||||
msg: "{{ data|ansible.utils.to_xml(indent='spaces', indent_width=2) }}"
|
||||
|
||||
# TASK [Define JSON data ] *************************************************************************
|
||||
# task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:5
|
||||
# ok: [localhost] => {
|
||||
# "ansible_facts": {
|
||||
# "data": {
|
||||
# "interface-configurations": {
|
||||
# "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg",
|
||||
# "interface-configuration": null
|
||||
# }
|
||||
# }
|
||||
# },
|
||||
# "changed": false
|
||||
# }
|
||||
# TASK [debug] ***********************************************************************************************************
|
||||
# task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:13
|
||||
# Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils
|
||||
# ok: [localhost] => {
|
||||
# "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<interface-configurations xmlns=\"http://cisco.com/ns/yang/
|
||||
# Cisco-IOS-XR-ifmgr-cfg\">\n <interface-configuration></interface-configuration>\n</interface-configurations>"
|
||||
# }
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,19 @@ DOCUMENTATION = """
|
|||
- Conversion library to use within the filter plugin.
|
||||
type: str
|
||||
default: xmltodict
|
||||
indent:
|
||||
description:
|
||||
- The character used for indentation (defaults to tabs).
|
||||
type: str
|
||||
default: tabs
|
||||
choices: ["tabs", "spaces"]
|
||||
indent_width:
|
||||
description:
|
||||
- The number of spaces to use to indent output data.
|
||||
- This option is only used when indent="spaces", otherwise it is ignored.
|
||||
- When indent="tabs", a single tab is always used for indentation.
|
||||
type: int
|
||||
default: 4
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
|
@ -104,6 +117,37 @@ EXAMPLES = r"""
|
|||
# Cisco-IOS-XR-ifmgr-cfg\">\n\t<interface-configuration></interface-configuration>\n</interface-configurations>"
|
||||
# }
|
||||
|
||||
#### example3 with indent='spaces' and indent_width=2
|
||||
|
||||
- name: Define JSON data
|
||||
ansible.builtin.set_fact:
|
||||
data:
|
||||
"interface-configurations":
|
||||
"@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"
|
||||
"interface-configuration":
|
||||
- debug:
|
||||
msg: "{{ data|ansible.utils.to_xml(indent='spaces', indent_width=2) }}"
|
||||
|
||||
# TASK [Define JSON data ] *************************************************************************
|
||||
# task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:5
|
||||
# ok: [localhost] => {
|
||||
# "ansible_facts": {
|
||||
# "data": {
|
||||
# "interface-configurations": {
|
||||
# "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg",
|
||||
# "interface-configuration": null
|
||||
# }
|
||||
# }
|
||||
# },
|
||||
# "changed": false
|
||||
# }
|
||||
# TASK [debug] ***********************************************************************************************************
|
||||
# task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:13
|
||||
# Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils
|
||||
# ok: [localhost] => {
|
||||
# "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<interface-configurations xmlns=\"http://cisco.com/ns/yang/
|
||||
# Cisco-IOS-XR-ifmgr-cfg\">\n <interface-configuration></interface-configuration>\n</interface-configurations>"
|
||||
# }
|
||||
"""
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
|
@ -123,7 +167,7 @@ except ImportError:
|
|||
@pass_environment
|
||||
def _to_xml(*args, **kwargs):
|
||||
"""Convert the given data from json to xml."""
|
||||
keys = ["data", "engine"]
|
||||
keys = ["data", "engine", "indent", "indent_width"]
|
||||
data = dict(zip(keys, args[1:]))
|
||||
data.update(kwargs)
|
||||
aav = AnsibleArgSpecValidator(data=data, schema=DOCUMENTATION, name="to_xml")
|
||||
|
|
|
@ -35,18 +35,26 @@ def _raise_error(msg):
|
|||
raise AnsibleFilterError(error)
|
||||
|
||||
|
||||
def to_xml(data, engine):
|
||||
def to_xml(data, engine, indent, indent_width):
|
||||
"""Convert data which is in json to xml"
|
||||
|
||||
:param data: The data passed in (data|to_xml(...))
|
||||
:type data: xml
|
||||
:param engine: Conversion library default=xmltodict
|
||||
:param indent: Indent char default='tabs'
|
||||
:param indent_width: Indent char multiplier default=4
|
||||
"""
|
||||
|
||||
indent_char = "\t"
|
||||
|
||||
if indent == "spaces":
|
||||
indent_char = " " * indent_width
|
||||
|
||||
if engine == "xmltodict":
|
||||
if not HAS_XMLTODICT:
|
||||
_raise_error("Missing required library xmltodict")
|
||||
try:
|
||||
res = xmltodict.unparse(data, pretty=True)
|
||||
res = xmltodict.unparse(data, pretty=True, indent=indent_char)
|
||||
except Exception:
|
||||
_raise_error("Input json is not valid")
|
||||
return res
|
||||
|
|
|
@ -18,11 +18,21 @@ from ansible_collections.ansible.utils.plugins.filter.to_xml import _to_xml
|
|||
INVALID_DATA = '<netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">'
|
||||
|
||||
VALID_DATA = {
|
||||
"interface-configurations": {"@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"},
|
||||
"interface-configurations": {
|
||||
"@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg",
|
||||
"key1": "value1",
|
||||
},
|
||||
}
|
||||
|
||||
OUTPUT = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"></interface-configurations>"""
|
||||
OUTPUT_TABS = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
|
||||
\t<key1>value1</key1>
|
||||
</interface-configurations>"""
|
||||
|
||||
OUTPUT_SPACES = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
|
||||
<key1>value1</key1>
|
||||
</interface-configurations>"""
|
||||
|
||||
|
||||
class TestToXml(unittest.TestCase):
|
||||
|
@ -44,7 +54,7 @@ class TestToXml(unittest.TestCase):
|
|||
self.maxDiff = None
|
||||
args = ["", VALID_DATA, "xmltodict"]
|
||||
result = _to_xml(*args)
|
||||
self.assertEqual(result, OUTPUT)
|
||||
self.assertEqual(result, OUTPUT_TABS)
|
||||
|
||||
def test_args(self):
|
||||
"""Check passing invalid argspec"""
|
||||
|
@ -65,3 +75,23 @@ class TestToXml(unittest.TestCase):
|
|||
with self.assertRaises(AnsibleError) as error:
|
||||
_to_xml(*args, **kwargs)
|
||||
self.assertIn("engine: test is not supported", str(error.exception))
|
||||
|
||||
def test_indent_with_spaces(self):
|
||||
"""Check passing indent with spaces and default indent_width"""
|
||||
self.maxDiff = None
|
||||
args = ["", VALID_DATA, "xmltodict", "spaces", 4]
|
||||
result = _to_xml(*args)
|
||||
self.assertEqual(result, OUTPUT_SPACES)
|
||||
|
||||
def test_invalid_indent(self):
|
||||
"""Check passing invalid indent value"""
|
||||
|
||||
# missing required arguments
|
||||
args = ["", VALID_DATA, "xmltodict", "test"]
|
||||
kwargs = {}
|
||||
with self.assertRaises(AnsibleError) as error:
|
||||
_to_xml(*args, **kwargs)
|
||||
self.assertIn(
|
||||
"value of indent must be one of: tabs, spaces, got: test",
|
||||
str(error.exception),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue