forked from debuggerx01/JSONFormat4Flutter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tools.py
250 lines (189 loc) · 8.49 KB
/
tools.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Filename : tools.py
# @Date : 18-1-30 上午11:48
# @Author : DebuggerX
import json
from PyQt5.QtWidgets import QLabel, QComboBox, QMessageBox
from template_code import get_top_code_dict, get_list_code_loop
msg_box_ui = None
# 验证json字符串是否合法
def is_json(myjson):
try:
j = json.loads(myjson)
except ValueError:
return False
if type(j) in (list, dict):
return True
return False
# 传入未格式化的单行json字符串,返回指定缩进的多行json字符串
def jformat(inp):
obj = json.loads(inp)
outp = json.dumps(obj, skipkeys=False, ensure_ascii=False, check_circular=True, allow_nan=True, cls=None, indent=' ', separators=None,
default=None, sort_keys=True)
return outp
def check_level_type(t):
if t in ('int', 'double', 'bool', 'Object'):
# 普通数据类型
return 1
if t == 'String':
# 字符串类型
return 2
if t.startswith('List<'):
# 数组类型
return 3
# 字典类型
return 4
def list_code_loop(code, count, total, n, ct):
list_count = 'List<' * (total - count)
ab_count = '>' * (total - count)
current_child = 'Child' * count
child_child = 'Child' * (count + 1)
current_items = 'Item' * (count + 1)
parent_items = 'Item' * count
fragment = get_list_code_loop(list_count, ct, ab_count, n, current_child, child_child, current_items, parent_items)
if code == '':
return fragment
else:
return code.replace('){\n', '){').replace('${loop}\n', fragment)
# 向代码模板中插入变量
def build_list_construction(t, n):
class_type = t.replace('List<', '').replace('>', '')
list_loop = 'jsonRes[\'%s\'] == null ? null : [];\n' % n
assert isinstance(t, str)
code = ''
total = t.count('>')
for i in range(total):
code = list_code_loop(code, i, total, n, class_type)
# 嵌套模板的后续处理
if check_level_type(class_type) not in (1, 2) and class_type != '':
code = code.replace('%s%s' % (n, 'Child' * total), '%s%s == null ? null : new %s.fromJson(%s%s)'
% (n, ('Item' * total), class_type, n, ('Item' * total)))
else:
code = code.replace('%s' % ('Child' * total), '%s' % ('Item' * total))
code = code[code.find(';') + 1:]
code = code.replace('%s){' % n, 'jsonRes[\'%s\']){' % n).replace('${loop}\n', '')
return list_loop + code
def add_param_to_code(code, param):
(f, t, n) = param
# 先按照基本数据类型方式处理
properties = ' %s %s;\n' % (t, n)
this_properties = 'this.%s, ' % n
construction = ' %s = jsonRes[\'%s\'];\n' % (n, f)
to_string = '"%s": $%s,' % (f, n)
pp = code.find('${properties}')
code = code[:pp] + properties + code[pp:]
ptp = code.find('${this.properties}')
code = code[:ptp] + this_properties + code[ptp:]
pc = code.find('${construction}')
code = code[:pc] + construction + code[pc:]
ps = code.find('${toString}')
code = code[:ps] + to_string + code[ps:]
# 字符串类型处理,只需要修改toString中的输出方式
t_code = check_level_type(t)
if t_code == 2:
code = code.replace(': $%s' % n, ': ${%s != null?\'${json.encode(%s)}\':\'null\'}' % (n, n))
# dict类型处理,只需要修改construction中的输出方式
elif t_code == 4:
code = code.replace('jsonRes[\'%s\']' % f, 'jsonRes[\'%s\'] == null ? null : new %s.fromJson(jsonRes[\'%s\'])' % (f, t, f))
# list类型处理,只需要修改construction中的输出方式
elif t_code == 3:
list_loop = build_list_construction(t, n)
code = code.replace('jsonRes[\'%s\'];' % f, list_loop)
return code
# 用来存储各个code片段,最后合并就是完整的代码
codes = []
def build_level_code(level_bean):
(l, f, t, n) = level_bean.pop(0)
type_code = check_level_type(t)
# 处理字典的子数据
if type_code == 4:
code = get_top_code_dict(t)
work_level = level_bean[0][0]
while len(level_bean) > 0:
(l, f, t, n) = level_bean.pop(0)
if n == '[list]':
continue
# 数据类型为字典时
if check_level_type(t) == 4:
# 先把该字典的定义作为顶层存到递归调用的bean顶部
child_bean = [(l, f, t, n)]
while len(level_bean) > 0 and level_bean[0][0] > work_level:
child_bean.append(level_bean.pop(0))
build_level_code(child_bean)
# 数据类型为数组时
if check_level_type(t) == 3 and len(level_bean) > 0:
generic_type = t.replace('List<', '').replace('>', '')
# 如果List的里层数据为dict则对其去壳后处理
if check_level_type(generic_type) == 4 and generic_type != '':
while check_level_type(level_bean[0][2]) == 3:
work_level = level_bean[0][0]
level_bean.pop(0)
child_bean = []
while len(level_bean) > 0 and level_bean[0][0] > work_level:
child_bean.append(level_bean.pop(0))
build_level_code(child_bean)
# 不管如何,到这里的数据都是目前dict的一级子数据,作为参数传入模板中
code = add_param_to_code(code, (f, t, n))
codes.append(code.replace(',${toString}', '').replace('${construction}', '').replace('${properties}', '').replace('${this.properties}', ''))
def generate_code(work_bean):
is_list_top = False
# 如果顶级容器为list而不是dict,则先在外面包一层dict,并将list的别名传递为dict的类型,list则重命名为'list',并修改标志位供后面修改使用
(l, f, t, n) = work_bean[0]
if t.startswith('List<'):
work_bean[0][3] = 'list'
work_bean[0][1] = 'json_list'
work_bean.insert(0, (-2, "", n, ""))
is_list_top = True
build_level_code(work_bean)
res = ''
codes.reverse()
for c in codes:
res += c
codes.clear()
# 如果顶级容器为list,需要修改第一次获取JSON对象的方式为直接获取
if is_list_top:
res = res.replace('jsonRes[\'list\']', 'jsonRes', 1)
# 如果json中存在空list这种操蛋情况,将list类型从list<>修改成list<dynamic>
res = res.replace('List<>', 'List<dynamic>')
# 移除参数构造函数用模板生成后多余的逗号和空格
res = res.replace(', });', '});')
# 移除没有必要的list取值循环
lines = res.splitlines()
for index in range(len(lines)):
if lines[index].strip() == '' and index < len(lines) - 1 and lines[index + 1].strip() in ('', '}'):
lines[index] = '//%s' % lines[index]
# 最终修改,添加json库导包代码,并为顶层对象增加默认构造
out_res = 'import \'dart:convert\' show json;\n'
first = True
for line in lines:
if line.startswith('//'):
continue
out_res += (line + '\n')
if first and r'.fromParams({this.' in line:
class_name = line.split(r'.fromParams({this.')[0].strip()
out_res += '\n factory %s(jsonStr) => jsonStr == null ? null : jsonStr is String ? new %s.fromJson(json.decode(jsonStr)) : ' \
'new %s.fromJson(jsonStr);\n' \
% (class_name, class_name, class_name)
first = False
return out_res
def check_and_generate_code(bean):
work_bean = []
global msg_box_ui
msg_box_ui = bean[0][1]
for f, t, n in bean:
field_text = f.text()
level = field_text.find('※') // 4
field_text = field_text[field_text.find("》") + 1: field_text.find(":") - 1] if ":" in field_text else ''
type_text = t.currentText() if type(t) is QComboBox else t.toPlainText()
name_text = n.text() if type(n) is QLabel else n.toPlainText()
if type_text.strip() != '':
work_bean.append([level, field_text, type_text, name_text])
else:
QMessageBox().information(msg_box_ui, "警告", "字段类型未设置", QMessageBox.Ok)
return ''
(l, f, t, n) = work_bean[0]
if t.startswith('List<') and n == '[list]':
QMessageBox().information(msg_box_ui, "警告", "请为顶层List设置别名,该别名将成为顶级对象类名", QMessageBox.Ok)
return ''
return generate_code(work_bean)