Skip to content

Commit d202832

Browse files
authored
Merge pull request #39 from alingse/fix-expand-restore
Fix expand restore && enhance is_array
2 parents 610a945 + d6fad56 commit d202832

File tree

8 files changed

+75
-43
lines changed

8 files changed

+75
-43
lines changed

README.rst

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ cat the raw.json to csv/xls use command line tool
2424

2525
.. code-block:: bash
2626
27-
cat raw.json| jsoncsv | mkexcel > output.csv
28-
cat raw.json| jsoncsv | mkexcel -t xls > output.xls
27+
cat raw.json | jsoncsv | mkexcel > output.csv
28+
cat raw.json | jsoncsv | mkexcel -t xls > output.xls
2929
3030
make sure each line of raw json text file is a json object
3131

@@ -87,7 +87,7 @@ just expand/restore the json, the expand json is one layer json.
8787
8888
jsoncsv raw.json expand.json
8989
jsoncsv -r expand.json raw.json
90-
cat raw.json|jsoncsv |jsoncsv -r > raw2.json
90+
cat raw.json | jsoncsv | jsoncsv -r > raw2.json
9191
9292
mkexcel the expanded json (one layer)
9393

@@ -104,17 +104,17 @@ expand json, 展开 json
104104

105105
.. code-block:: bash
106106
107-
jsoncsv -e raw.json expand.json
108-
cat raw.json expand.json
107+
$jsoncsv -e raw.json expand.json
108+
$cat raw.json expand.json
109109
{"s":[1,2,{"w":1}]}
110110
{"s.2.w": 1,"s.0": 1,"s.1": 2}
111111
112112
113-
{"s":[1,2,{"w":1}]} transformed to {"s.2.w": 1,"s.0": 1,"s.1": 2}
113+
{"s":[1,2,{"w":1}]} will transformed to {"s.2.w": 1,"s.0": 1,"s.1": 2}
114114

115-
expand.json is only one layer json, it can be easy change to csv or xlsx
115+
the output "expand.json" is only one layer json, it can be easy change to csv or xlsx (with `mkexcel`)
116116

117-
-r,--restore
117+
-r, --restore
118118
---------------
119119
restore the expanded json 重构被展开的json
120120

@@ -127,7 +127,7 @@ restore the expanded json 重构被展开的json
127127
128128
{"s.2.w": 1,"s.0": 1,"s.1": 2} change to {"s":[1,2,{"w":1}]}
129129

130-
-s,--separator
130+
-s, --separator
131131
---------------
132132

133133
separator used for combine the keys in the tree
@@ -156,7 +156,7 @@ dump expanded (by **jsoncsv**) json file to csv or xls file
156156
157157
mkexcel expand.json output.csv
158158
159-
-t,--type
159+
-t, --type
160160
--------------
161161

162162
chose dump type in ['csv', 'xls'] default is 'csv'
@@ -191,3 +191,9 @@ wait next next version
191191
-----------------------------------------
192192
193193
but better than python strand library csv.
194+
195+
4. Windows is poor support
196+
-----------------------------------------
197+
see https://github.com/alingse/jsoncsv/issues/37
198+
199+
try use https://jsoncsv.jsonutil.online/ instead

fixture/files/raw.2.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
["a", "ab", "b", "a", "ab", "b", "a", "ab", "b", "a", "ab", "b"]
2+
["c", "ab", "b", "a", "ab", "b", "a", "ab", "b", "a", "ab", "c"]

jsoncsv/jsontool.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from __future__ import unicode_literals
66

77
import json
8-
98
from copy import deepcopy
109
from itertools import groupby
1110
from operator import itemgetter
@@ -26,7 +25,6 @@ def gen_leaf(root, path=None):
2625
if path is None:
2726
path = []
2827

29-
# the leaf
3028
if not isinstance(root, (dict, list)) or not root:
3129
leaf = (path, root)
3230
yield leaf
@@ -36,7 +34,6 @@ def gen_leaf(root, path=None):
3634
items = root.iteritems()
3735
else:
3836
items = root.items()
39-
4037
else:
4138
items = enumerate(root)
4239

@@ -47,14 +44,17 @@ def gen_leaf(root, path=None):
4744
yield leaf
4845

4946

50-
def is_array(keys, ensure_str=True):
51-
copy_keys = list(deepcopy(keys))
52-
int_keys = list(range(len(copy_keys)))
53-
if copy_keys == int_keys:
47+
def is_array_index(keys, enable_str=True):
48+
keys = list(deepcopy(keys))
49+
# 不强调有序
50+
key_map = {key: True for key in keys}
51+
int_keys = range(len(keys))
52+
53+
if all(key in key_map for key in int_keys):
5454
return True
55-
if ensure_str:
56-
str_keys = list(map(str, int_keys))
57-
if copy_keys == str_keys:
55+
56+
if enable_str:
57+
if all(str(key) in key_map for key in int_keys):
5858
return True
5959
return False
6060

@@ -84,7 +84,7 @@ def from_leaf(leafs):
8484
child.append((head, _child))
8585

8686
child_keys = map(_get_head, child)
87-
if is_array(child_keys):
87+
if is_array_index(child_keys):
8888
child.sort(key=lambda x: int(x[0]))
8989
return list(map(_get_leaf, child))
9090

@@ -161,6 +161,7 @@ def gen_objs_from_array():
161161
content = json.dumps(new, ensure_ascii=False)
162162
if PY2:
163163
fout.write(content.encode('utf-8'))
164+
fout.write('\n'.encode('utf-8'))
164165
else:
165166
fout.write(content)
166-
fout.write(str('\n'))
167+
fout.write('\n')

jsoncsv/main.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@
77
from jsoncsv import dumptool
88
from jsoncsv.dumptool import dump_excel
99
from jsoncsv.jsontool import convert_json
10-
from jsoncsv.utils import separator_type
10+
from jsoncsv.utils import unit_char
11+
12+
13+
def separator_type(sep):
14+
if len(sep) != 1:
15+
raise click.BadOptionUsage('separator can only be a char')
16+
if sep == unit_char:
17+
raise click.BadOptionUsage('separator can not be `\\` ')
18+
return sep
1119

1220

1321
@click.command()

jsoncsv/utils.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,9 @@
22
# author@alingse
33
# 2016.11.20
44

5-
import click
6-
7-
__all__ = []
8-
95
unit_char = '\\'
106

117

12-
def separator_type(sep):
13-
if len(sep) > 1:
14-
raise click.BadOptionUsage('separator can only be a char')
15-
if sep == unit_char:
16-
raise click.BadOptionUsage('separator can not be `\\` ')
17-
return sep
18-
19-
208
def encode_safe_key(path, separator):
219
path = [p.replace(unit_char, unit_char * 2) for p in path]
2210
separator = unit_char + separator

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
setup(
1414
name='jsoncsv',
15-
version='2.2.2',
15+
version='2.2.3',
1616
url='https://github.com/alingse/jsoncsv',
1717
description='A command tool easily convert json file to csv or xlsx.',
1818
long_description=readme,

tests/test_jsoncsv.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# coding=utf-8
22
# author@alingse
33
# 2016.08.09
4-
4+
import io
5+
import json
56
import unittest
67
from click.testing import CliRunner
78

@@ -21,3 +22,18 @@ def test_jsoncsv_expand_with_json_array(self):
2122
args = ['-e', 'fixture/files/raw.1.json', 'fixture/files/tmp.expand.1.json', '-A']
2223
result = runner.invoke(jsoncsv, args=args)
2324
assert result.exit_code == 0
25+
26+
def test_jsoncsv_expand_restore(self):
27+
runner = CliRunner(echo_stdin=True)
28+
result = runner.invoke(jsoncsv, args=['-e', 'fixture/files/raw.2.json', 'fixture/files/tmp.expand.2.json'])
29+
assert result.exit_code == 0
30+
result = runner.invoke(jsoncsv, args=['-r', 'fixture/files/tmp.expand.2.json', 'fixture/files/tmp.restore.2.json'])
31+
assert result.exit_code == 0
32+
33+
with io.open('fixture/files/raw.2.json', 'r') as f:
34+
input_data = [json.loads(line) for line in f]
35+
36+
with io.open('fixture/files/tmp.restore.2.json', 'r') as f:
37+
resotre_data = [json.loads(line) for line in f]
38+
39+
self.assertEqual(input_data, resotre_data)

tests/test_jsontool.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from jsoncsv import PY2
1010
from jsoncsv.jsontool import expand, restore
11-
from jsoncsv.jsontool import is_array
11+
from jsoncsv.jsontool import is_array_index
1212
from jsoncsv.jsontool import convert_json
1313

1414

@@ -63,11 +63,13 @@ def test_complex(self):
6363

6464
self.assertListEqual(s[4], _s[4])
6565

66-
def test_is_array(self):
67-
assert is_array([0, 1, 2, 3])
68-
assert is_array(['0', '1', '2', '3'])
69-
assert not is_array([1, 2, 3])
70-
assert not is_array(['0', 1, 2])
66+
def test_is_array_index(self):
67+
self.assertTrue(is_array_index([0, 1, 2, 3]))
68+
self.assertTrue(is_array_index(['0', '1', '2', '3']))
69+
# string order
70+
self.assertTrue(is_array_index(['0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9']))
71+
self.assertFalse(is_array_index([1, 2, 3]))
72+
self.assertFalse(is_array_index(['0', 1, 2]))
7173

7274
def test_unicode(self):
7375
data = [
@@ -90,6 +92,15 @@ def test_expand_with_safe(self):
9092
origin = restore(expobj, safe=True)
9193
self.assertEqual(origin, data)
9294

95+
def test_expand_and_restore(self):
96+
data = ["a", "ab", "b"] * 4
97+
expobj = expand(data)
98+
self.assertEqual(expobj["0"], "a")
99+
self.assertEqual(expobj["1"], "ab")
100+
101+
origin = restore(expobj)
102+
self.assertEqual(data, origin)
103+
93104

94105
class TestConvertJSON(unittest.TestCase):
95106

0 commit comments

Comments
 (0)