1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-06-17 00:07:37 +02:00

add raw option to xml parser for _ attribute prefix

This commit is contained in:
Kelly Brazil
2022-11-01 18:09:05 -07:00
parent de7a010f62
commit 186ad73651
6 changed files with 94 additions and 13 deletions

View File

@ -5,6 +5,11 @@
jc - JSON Convert `XML` file parser jc - JSON Convert `XML` file parser
This parser adds a `@` prefix to attributes by default. This can be changed
to a `_` prefix by using the `-r` (cli) or `raw=True` (module) option.
Text values for nodes will have the key-name of `#text`.
Usage (cli): Usage (cli):
$ cat foo.xml | jc --xml $ cat foo.xml | jc --xml
@ -93,4 +98,4 @@ Returns:
### Parser Information ### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com) Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@ -1,5 +1,10 @@
"""jc - JSON Convert `XML` file parser """jc - JSON Convert `XML` file parser
This parser adds a `@` prefix to attributes by default. This can be changed
to a `_` prefix by using the `-r` (cli) or `raw=True` (module) option.
Text values for nodes will have the key-name of `#text`.
Usage (cli): Usage (cli):
$ cat foo.xml | jc --xml $ cat foo.xml | jc --xml
@ -68,10 +73,15 @@ Examples:
import jc.utils import jc.utils
from jc.exceptions import LibraryNotInstalled from jc.exceptions import LibraryNotInstalled
try:
import xmltodict
except Exception:
raise LibraryNotInstalled('The xmltodict library is not installed.')
class info(): class info():
"""Provides parser metadata (version, author, etc.)""" """Provides parser metadata (version, author, etc.)"""
version = '1.6' version = '1.7'
description = 'XML file parser' description = 'XML file parser'
author = 'Kelly Brazil' author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com' author_email = 'kellyjonbrazil@gmail.com'
@ -82,7 +92,7 @@ class info():
__version__ = info.version __version__ = info.version
def _process(proc_data): def _process(proc_data, has_data=False):
""" """
Final processing to conform to the schema. Final processing to conform to the schema.
@ -94,9 +104,13 @@ def _process(proc_data):
Dictionary representing an XML document. Dictionary representing an XML document.
""" """
raw_output = []
# No further processing if has_data:
return proc_data # standard output with @ prefix for attributes
raw_output = xmltodict.parse(proc_data)
return raw_output
def parse(data, raw=False, quiet=False): def parse(data, raw=False, quiet=False):
@ -113,22 +127,19 @@ def parse(data, raw=False, quiet=False):
Dictionary. Raw or processed structured data. Dictionary. Raw or processed structured data.
""" """
# check if xml library is installed and fail gracefully if it is not
try:
import xmltodict
except Exception:
raise LibraryNotInstalled('The xmltodict library is not installed.')
jc.utils.compatibility(__name__, info.compatible, quiet) jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data) jc.utils.input_type_check(data)
raw_output = [] raw_output = []
has_data = False
if jc.utils.has_data(data): if jc.utils.has_data(data):
has_data = True
raw_output = xmltodict.parse(data) # modified output with _ prefix for attributes
raw_output = xmltodict.parse(data, attr_prefix='_')
if raw: if raw:
return raw_output return raw_output
else: else:
return _process(raw_output) return _process(data, has_data)

View File

@ -0,0 +1 @@
{"nmaprun":{"_scanner":"nmap","_args":"nmap -oX - -p 443 galaxy.ansible.com","_start":"1666781498","_startstr":"Wed Oct 26 11:51:38 2022","_version":"7.92","_xmloutputversion":"1.05","scaninfo":{"_type":"connect","_protocol":"tcp","_numservices":"1","_services":"443"},"verbose":{"_level":"0"},"debugging":{"_level":"0"},"hosthint":{"status":{"_state":"up","_reason":"unknown-response","_reason_ttl":"0"},"address":{"_addr":"172.67.68.251","_addrtype":"ipv4"},"hostnames":{"hostname":{"_name":"galaxy.ansible.com","_type":"user"}}},"host":{"_starttime":"1666781498","_endtime":"1666781498","status":{"_state":"up","_reason":"syn-ack","_reason_ttl":"0"},"address":{"_addr":"172.67.68.251","_addrtype":"ipv4"},"hostnames":{"hostname":[{"_name":"galaxy.ansible.com","_type":"user"},{"_name":"galaxy.ansible.com","_type":"PTR"}]},"ports":{"port":{"_protocol":"tcp","_portid":"443","state":{"_state":"open","_reason":"syn-ack","_reason_ttl":"0"},"service":{"_name":"https","_method":"table","_conf":"3"}}},"times":{"_srtt":"12260","_rttvar":"9678","_to":"100000"}},"runstats":{"finished":{"_time":"1666781498","_timestr":"Wed Oct 26 11:51:38 2022","_summary":"Nmap done at Wed Oct 26 11:51:38 2022; 1 IP address (1 host up) scanned in 0.10 seconds","_elapsed":"0.10","_exit":"success"},"hosts":{"_up":"1","_down":"0","_total":"1"}}}}

1
tests/fixtures/generic/xml-nmap.json vendored Normal file
View File

@ -0,0 +1 @@
{"nmaprun":{"@scanner":"nmap","@args":"nmap -oX - -p 443 galaxy.ansible.com","@start":"1666781498","@startstr":"Wed Oct 26 11:51:38 2022","@version":"7.92","@xmloutputversion":"1.05","scaninfo":{"@type":"connect","@protocol":"tcp","@numservices":"1","@services":"443"},"verbose":{"@level":"0"},"debugging":{"@level":"0"},"hosthint":{"status":{"@state":"up","@reason":"unknown-response","@reason_ttl":"0"},"address":{"@addr":"172.67.68.251","@addrtype":"ipv4"},"hostnames":{"hostname":{"@name":"galaxy.ansible.com","@type":"user"}}},"host":{"@starttime":"1666781498","@endtime":"1666781498","status":{"@state":"up","@reason":"syn-ack","@reason_ttl":"0"},"address":{"@addr":"172.67.68.251","@addrtype":"ipv4"},"hostnames":{"hostname":[{"@name":"galaxy.ansible.com","@type":"user"},{"@name":"galaxy.ansible.com","@type":"PTR"}]},"ports":{"port":{"@protocol":"tcp","@portid":"443","state":{"@state":"open","@reason":"syn-ack","@reason_ttl":"0"},"service":{"@name":"https","@method":"table","@conf":"3"}}},"times":{"@srtt":"12260","@rttvar":"9678","@to":"100000"}},"runstats":{"finished":{"@time":"1666781498","@timestr":"Wed Oct 26 11:51:38 2022","@summary":"Nmap done at Wed Oct 26 11:51:38 2022; 1 IP address (1 host up) scanned in 0.10 seconds","@elapsed":"0.10","@exit":"success"},"hosts":{"@up":"1","@down":"0","@total":"1"}}}}

36
tests/fixtures/generic/xml-nmap.xml vendored Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nmaprun>
<?xml-stylesheet href="file:///usr/bin/../share/nmap/nmap.xsl" type="text/xsl"?>
<!-- Nmap 7.92 scan initiated Wed Oct 26 11:51:38 2022 as: nmap -oX - -p 443 galaxy.ansible.com -->
<nmaprun scanner="nmap" args="nmap -oX - -p 443 galaxy.ansible.com" start="1666781498" startstr="Wed Oct 26 11:51:38 2022" version="7.92" xmloutputversion="1.05">
<scaninfo type="connect" protocol="tcp" numservices="1" services="443"/>
<verbose level="0"/>
<debugging level="0"/>
<hosthint>
<status state="up" reason="unknown-response" reason_ttl="0"/>
<address addr="172.67.68.251" addrtype="ipv4"/>
<hostnames>
<hostname name="galaxy.ansible.com" type="user"/>
</hostnames>
</hosthint>
<host starttime="1666781498" endtime="1666781498">
<status state="up" reason="syn-ack" reason_ttl="0"/>
<address addr="172.67.68.251" addrtype="ipv4"/>
<hostnames>
<hostname name="galaxy.ansible.com" type="user"/>
<hostname name="galaxy.ansible.com" type="PTR"/>
</hostnames>
<ports>
<port protocol="tcp" portid="443">
<state state="open" reason="syn-ack" reason_ttl="0"/>
<service name="https" method="table" conf="3"/>
</port>
</ports>
<times srtt="12260" rttvar="9678" to="100000"/>
</host>
<runstats>
<finished time="1666781498" timestr="Wed Oct 26 11:51:38 2022" summary="Nmap done at Wed Oct 26 11:51:38 2022; 1 IP address (1 host up) scanned in 0.10 seconds" elapsed="0.10" exit="success"/>
<hosts up="1" down="0" total="1"/>
</runstats>
</nmaprun>

View File

@ -15,6 +15,9 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-foodmenu.xml'), 'r', encoding='utf-8') as f: with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-foodmenu.xml'), 'r', encoding='utf-8') as f:
generic_xml_foodmenu = f.read() generic_xml_foodmenu = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-nmap.xml'), 'r', encoding='utf-8') as f:
generic_xml_nmap = f.read()
# output # output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-cd_catalog.json'), 'r', encoding='utf-8') as f: with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-cd_catalog.json'), 'r', encoding='utf-8') as f:
generic_xml_cd_catalog_json = json.loads(f.read()) generic_xml_cd_catalog_json = json.loads(f.read())
@ -22,6 +25,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-foodmenu.json'), 'r', encoding='utf-8') as f: with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-foodmenu.json'), 'r', encoding='utf-8') as f:
generic_xml_foodmenu_json = json.loads(f.read()) generic_xml_foodmenu_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-nmap.json'), 'r', encoding='utf-8') as f:
generic_xml_nmap_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-nmap-raw.json'), 'r', encoding='utf-8') as f:
generic_xml_nmap_r_json = json.loads(f.read())
def test_xml_nodata(self): def test_xml_nodata(self):
""" """
@ -29,6 +38,12 @@ class MyTests(unittest.TestCase):
""" """
self.assertEqual(jc.parsers.xml.parse('', quiet=True), []) self.assertEqual(jc.parsers.xml.parse('', quiet=True), [])
def test_xml_nodata_r(self):
"""
Test xml parser with no data and raw output
"""
self.assertEqual(jc.parsers.xml.parse('', raw=True, quiet=True), [])
def test_xml_cd_catalog(self): def test_xml_cd_catalog(self):
""" """
Test the cd catalog xml file Test the cd catalog xml file
@ -41,6 +56,18 @@ class MyTests(unittest.TestCase):
""" """
self.assertEqual(jc.parsers.xml.parse(self.generic_xml_foodmenu, quiet=True), self.generic_xml_foodmenu_json) self.assertEqual(jc.parsers.xml.parse(self.generic_xml_foodmenu, quiet=True), self.generic_xml_foodmenu_json)
def test_xml_nmap(self):
"""
Test nmap xml output
"""
self.assertEqual(jc.parsers.xml.parse(self.generic_xml_nmap, quiet=True), self.generic_xml_nmap_json)
def test_xml_nmap_r(self):
"""
Test nmap xml raw output
"""
self.assertEqual(jc.parsers.xml.parse(self.generic_xml_nmap, raw=True, quiet=True), self.generic_xml_nmap_r_json)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()