Skip to content

Commit

Permalink
Add simple CLI utility by @tribals
Browse files Browse the repository at this point in the history
  • Loading branch information
sanand0 committed Nov 21, 2018
1 parent d6651ce commit e910f4d
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 4 deletions.
37 changes: 36 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,45 @@ The `Parker`_ convention absorbs the root element by default.
Installation
------------

This is a pure-Python package built for Python 2.6+ and Python 3.0+. To set up::
This is a pure-Python package built for Python 2.7+ and Python 3.0+. To set up::

pip install xmljson


Simple CLI utility
------------------

After installation, you can benefit from using this package as simple CLI utility. By now only XML to JSON conversion supported. Example::

$ python -m xmljson -h
usage: xmljson [-h] [-o OUT_FILE]
[-d {abdera,badgerfish,cobra,gdata,parker,xmldata,yahoo}]
[in_file]

positional arguments:
in_file defaults to stdin

optional arguments:
-h, --help show this help message and exit
-o OUT_FILE, --out_file OUT_FILE
defaults to stdout
-d {abdera,badgerfish,...}, --dialect {...}
defaults to parker

$ python -m xmljson -d parker tests/mydata.xml
{
"foo": "spam",
"bar": 42
}

This is a typical UNIX filter program: it reads file (or ``stdin``), processes it in some way (convert XML to JSON in this case), then prints it to ``stdout`` (or file). Example with pipe::

$ some-xml-producer | python -m xmljson | some-json-processor

There is also ``pip``'s ``console_script`` entry-point, you can call this utility as ``xml2json``::

$ xml2json -d abdera mydata.xml

Roadmap
-------

Expand Down
11 changes: 8 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import io

try:
from setuptools import setup
except ImportError:
from distutils.core import setup
import xmljson

with open('README.rst') as readme_file:
with io.open('README.rst', encoding='utf-8') as readme_file:
readme = readme_file.read()

with open('HISTORY.rst') as history_file:
with io.open('HISTORY.rst', encoding='utf-8') as history_file:
history = history_file.read().replace('.. :changelog:', '')

setup(
Expand Down Expand Up @@ -46,4 +46,9 @@
],
test_suite='tests',
tests_require=['lxml'],
entry_points={
'console_scripts': [
'xml2json = xmljson.__main__:main'
]
}
)
30 changes: 30 additions & 0 deletions tests/test_xmljson.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,53 @@
import lxml.etree
import xml.etree.cElementTree
import xmljson
from xmljson.__main__ import main, parse, closing

_folder = os.path.dirname(os.path.abspath(__file__))

# For Python 3, decode byte strings as UTF-8
if sys.version_info[0] == 3:
def decode(s):
return s.decode('utf-8')

def openwrite(path):
return io.open(path, 'w', encoding='utf-8')

elif sys.version_info[0] == 2:
def decode(s):
return s

def openwrite(path):
return io.open(path, 'wb')


def read(path):
with io.open(os.path.join(_folder, path), 'r', encoding='utf-8') as handle:
return handle.read()


class TestCLI(unittest.TestCase):
tmp = os.path.join(_folder, 'delete-output.json')

def test_cli(self):
dialects = [xmljson.Abdera(), xmljson.BadgerFish(), xmljson.Cobra(),
xmljson.GData(), xmljson.Parker(), xmljson.Yahoo()]

for dialect in dialects:
for path in ['abdera-1.xml', 'abdera-2.xml', 'abdera-3.xml', 'abdera-4.xml']:
in_file = io.open(os.path.join(_folder, path), encoding='utf-8')
out_file = openwrite(self.tmp)
main(in_file, out_file, dialect)
in_file = io.open(os.path.join(_folder, path), encoding='utf-8')
out_file = io.open(self.tmp, encoding='utf-8')
with closing(in_file), closing(out_file):
self.assertEqual(json.load(out_file), dialect.data(parse(in_file).getroot()))

def tearDown(self):
if os.path.exists(self.tmp):
os.remove(self.tmp)


class TestXmlJson(unittest.TestCase):

def setUp(self):
Expand Down
43 changes: 43 additions & 0 deletions xmljson/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import sys
import json
import argparse
from contextlib import closing
import xmljson

try:
from lxml.etree import parse
except ImportError:
from xml.etree.cElementTree import parse

dialects = {
key.lower(): val for key, val in sorted(vars(xmljson).items())
if isinstance(val, type) and issubclass(val, xmljson.XMLData)
}


def parse_args(args=None, in_file=sys.stdin, out_file=sys.stdout):
parser = argparse.ArgumentParser(prog='xmljson')
parser.add_argument('in_file', type=argparse.FileType(), nargs='?', default=in_file,
help='defaults to stdin')
parser.add_argument('-o', '--out_file', type=argparse.FileType('w'), default=out_file,
help='defaults to stdout')
parser.add_argument('-d', '--dialect', choices=list(dialects.keys()), default='parker',
type=str.lower, help='defaults to parker')
args = parser.parse_args() if args is None else parser.parse_args(args)

if args.dialect not in dialects:
raise TypeError('Unknown dialect: %s' % args.dialect)
else:
dialect = dialects[args.dialect]()

return args.in_file, args.out_file, dialect


def main(*test_args):
in_file, out_file, dialect = parse_args() if not test_args else test_args
with closing(in_file) as in_file, closing(out_file) as out_file:
json.dump(dialect.data(parse(in_file).getroot()), out_file, indent=2)


if __name__ == '__main__':
main()

0 comments on commit e910f4d

Please sign in to comment.