json-duplicate-keys 2025.6.6__py3-none-any.whl → 2025.8.19__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- json_duplicate_keys/__init__.py +78 -47
- {json_duplicate_keys-2025.6.6.dist-info → json_duplicate_keys-2025.8.19.dist-info}/METADATA +92 -83
- json_duplicate_keys-2025.8.19.dist-info/RECORD +6 -0
- json_duplicate_keys-2025.6.6.dist-info/RECORD +0 -6
- {json_duplicate_keys-2025.6.6.dist-info → json_duplicate_keys-2025.8.19.dist-info}/LICENSE +0 -0
- {json_duplicate_keys-2025.6.6.dist-info → json_duplicate_keys-2025.8.19.dist-info}/WHEEL +0 -0
- {json_duplicate_keys-2025.6.6.dist-info → json_duplicate_keys-2025.8.19.dist-info}/top_level.txt +0 -0
json_duplicate_keys/__init__.py
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
json_duplicate_keys_VERSION = "2025.6.6"
|
3
2
|
try:
|
4
|
-
unicode
|
3
|
+
unicode
|
5
4
|
except NameError:
|
6
|
-
unicode = str
|
5
|
+
unicode = str
|
7
6
|
|
8
7
|
from collections import OrderedDict
|
9
8
|
import json, re, copy
|
@@ -23,7 +22,7 @@ def normalize_key(name, dupSign_start="{{{", dupSign_end="}}}", _isDebug_=False)
|
|
23
22
|
|
24
23
|
if type(dupSign_end) not in [str, unicode]: dupSign_end = "}}}"
|
25
24
|
|
26
|
-
return re.sub('
|
25
|
+
return re.sub(re.escape(dupSign_start)+'_\\d+_'+re.escape(dupSign_end)+'$', "", name)
|
27
26
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
28
27
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
29
28
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -36,10 +35,12 @@ def loads(Jstr, dupSign_start="{{{", dupSign_end="}}}", ordered_dict=False, skip
|
|
36
35
|
# User input data type validation
|
37
36
|
if type(_isDebug_) != bool: _isDebug_ = False
|
38
37
|
|
38
|
+
if type(skipDuplicated) != bool: skipDuplicated = False
|
39
|
+
|
39
40
|
if type(ordered_dict) != bool: ordered_dict = False
|
40
41
|
|
41
|
-
if type(Jstr) not in [str, unicode]:
|
42
|
-
if _isDebug_: print("\x1b[31m[-] DataTypeError: the JSON object must be str or
|
42
|
+
if type(Jstr) not in [str, unicode, bytes]:
|
43
|
+
if _isDebug_: print("\x1b[31m[-] DataTypeError: the JSON object must be str, unicode or bytes, not {}\x1b[0m".format(type(Jstr)))
|
43
44
|
return False
|
44
45
|
|
45
46
|
if type(dupSign_start) not in [str, unicode]: dupSign_start = "{{{"
|
@@ -95,7 +96,11 @@ def loads(Jstr, dupSign_start="{{{", dupSign_end="}}}", ordered_dict=False, skip
|
|
95
96
|
if ordered_dict: Jloads = json.loads(Jstr, object_pairs_hook=OrderedDict)
|
96
97
|
|
97
98
|
if skipDuplicated:
|
98
|
-
|
99
|
+
if type(Jloads) in [list, dict, OrderedDict]:
|
100
|
+
return JSON_DUPLICATE_KEYS(Jloads)
|
101
|
+
else:
|
102
|
+
if _isDebug_: print("\x1b[31m[-] DataError: Invalid JSON format\x1b[0m")
|
103
|
+
return False
|
99
104
|
|
100
105
|
if type(Jloads) in [list, dict, OrderedDict]:
|
101
106
|
dupSign_start_escape = "".join(["\\\\u"+hex(ord(c))[2:].zfill(4) for c in dupSign_start])
|
@@ -105,23 +110,41 @@ def loads(Jstr, dupSign_start="{{{", dupSign_end="}}}", ordered_dict=False, skip
|
|
105
110
|
dupSign_end_escape_regex = re.escape(dupSign_end)
|
106
111
|
|
107
112
|
|
108
|
-
|
109
|
-
|
110
|
-
|
113
|
+
if type(Jstr) == bytes:
|
114
|
+
Jstr = re.sub(r'\\\\'.encode(), '\x00\x01'.encode(), Jstr)
|
115
|
+
Jstr = re.sub(r'\\"'.encode(), '\x02\x03'.encode(), Jstr)
|
116
|
+
Jstr = re.sub(r'"([^"]*)"[\s\t\r\n]*([,\]}])'.encode(), '\x04\x05\\1\x04\x05\\2'.encode(), Jstr)
|
117
|
+
|
111
118
|
|
119
|
+
Jstr = re.sub(r'"([^"]+)"[\s\t\r\n]*:'.encode(), (r'"\1'+dupSign_start_escape+'_dupSign_'+dupSign_end_escape+'":').encode(), Jstr)
|
120
|
+
|
121
|
+
Jstr = re.sub(r'""[\s\t\r\n]*:'.encode(), ('"'+dupSign_start_escape+'_dupSign_'+dupSign_end_escape+'":').encode(), Jstr)
|
122
|
+
|
123
|
+
i = 0
|
124
|
+
while re.search((dupSign_start_escape+'_dupSign_'+dupSign_end_escape+r'"[\s\t\r\n]*:').encode(), Jstr):
|
125
|
+
Jstr = re.sub((dupSign_start_escape+'_dupSign_'+dupSign_end_escape+r'"[\s\t\r\n]*:').encode(), (dupSign_start_escape+"_"+str(i)+"_"+dupSign_end_escape+'":').encode(), Jstr, 1)
|
126
|
+
i += 1
|
127
|
+
|
128
|
+
Jstr = re.sub('\x00\x01'.encode(), r'\\\\'.encode(), Jstr)
|
129
|
+
Jstr = re.sub('\x02\x03'.encode(), r'\\"'.encode(), Jstr)
|
130
|
+
Jstr = re.sub('\x04\x05'.encode(), r'"'.encode(), Jstr)
|
131
|
+
else:
|
132
|
+
Jstr = re.sub(r'\\\\', '\x00\x01', Jstr)
|
133
|
+
Jstr = re.sub(r'\\"', '\x02\x03', Jstr)
|
134
|
+
Jstr = re.sub(r'"([^"]*)"[\s\t\r\n]*([,\]}])', '\x04\x05\\1\x04\x05\\2', Jstr)
|
112
135
|
|
113
|
-
|
136
|
+
Jstr = re.sub(r'"([^"]+)"[\s\t\r\n]*:', r'"\1'+dupSign_start_escape+'_dupSign_'+dupSign_end_escape+'":', Jstr)
|
114
137
|
|
115
|
-
|
138
|
+
Jstr = re.sub(r'""[\s\t\r\n]*:', '"'+dupSign_start_escape+'_dupSign_'+dupSign_end_escape+'":', Jstr)
|
116
139
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
140
|
+
i = 0
|
141
|
+
while re.search(dupSign_start_escape+'_dupSign_'+dupSign_end_escape+r'"[\s\t\r\n]*:', Jstr):
|
142
|
+
Jstr = re.sub(dupSign_start_escape+'_dupSign_'+dupSign_end_escape+r'"[\s\t\r\n]*:', dupSign_start_escape+"_"+str(i)+"_"+dupSign_end_escape+'":', Jstr, 1)
|
143
|
+
i += 1
|
121
144
|
|
122
|
-
|
123
|
-
|
124
|
-
|
145
|
+
Jstr = re.sub('\x00\x01', r'\\\\', Jstr)
|
146
|
+
Jstr = re.sub('\x02\x03', r'\\"', Jstr)
|
147
|
+
Jstr = re.sub('\x04\x05', r'"', Jstr)
|
125
148
|
|
126
149
|
Jloads = json.loads(Jstr)
|
127
150
|
if ordered_dict:
|
@@ -153,8 +176,10 @@ def loads(Jstr, dupSign_start="{{{", dupSign_end="}}}", ordered_dict=False, skip
|
|
153
176
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
154
177
|
def load(Jfilepath, dupSign_start="{{{", dupSign_end="}}}", ordered_dict=False, skipDuplicated=False, _isDebug_=False):
|
155
178
|
try:
|
156
|
-
|
157
|
-
Jstr = Jfile.read()
|
179
|
+
try:
|
180
|
+
with open(Jfilepath) as Jfile: Jstr = Jfile.read()
|
181
|
+
except Exception as e:
|
182
|
+
with open(Jfilepath, "rb") as Jfile: Jstr = Jfile.read()
|
158
183
|
|
159
184
|
return loads(Jstr, dupSign_start=dupSign_start, dupSign_end=dupSign_end, ordered_dict=ordered_dict, skipDuplicated=skipDuplicated, _isDebug_=_isDebug_)
|
160
185
|
except Exception as e:
|
@@ -192,7 +217,7 @@ class JSON_DUPLICATE_KEYS:
|
|
192
217
|
# User input data type validation
|
193
218
|
if type(_isDebug_) != bool: _isDebug_ = False
|
194
219
|
|
195
|
-
if type(name) not in [str, unicode]:
|
220
|
+
if type(name) not in [str, unicode]:
|
196
221
|
if _isDebug_: print("\x1b[31m[-] DataTypeError: the KEY name must be str or unicode, not {}\x1b[0m".format(type(name)))
|
197
222
|
return {"name":name, "value":"JSON_DUPLICATE_KEYS_ERROR"}
|
198
223
|
|
@@ -215,16 +240,18 @@ class JSON_DUPLICATE_KEYS:
|
|
215
240
|
Jname = []
|
216
241
|
Jval = self.__Jobj
|
217
242
|
name_split = name.split(separator)
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
243
|
+
|
244
|
+
if type(Jobj.getObject()) in [dict, OrderedDict]:
|
245
|
+
for k in Jobj.getObject().keys():
|
246
|
+
if len(k.split(separator)) >= len(name_split):
|
247
|
+
if case_insensitive:
|
248
|
+
if separator.join(k.split(separator)[:len(name_split)]).lower() == name.lower():
|
249
|
+
Jname = k.split(separator)[:len(name_split)]
|
250
|
+
break
|
251
|
+
else:
|
252
|
+
if separator.join(k.split(separator)[:len(name_split)]) == name:
|
253
|
+
Jname = name_split
|
254
|
+
break
|
228
255
|
|
229
256
|
if len(Jname) > 0:
|
230
257
|
for k in Jname:
|
@@ -250,7 +277,7 @@ class JSON_DUPLICATE_KEYS:
|
|
250
277
|
|
251
278
|
if type(ordered_dict) != bool: ordered_dict = False
|
252
279
|
|
253
|
-
if type(name) not in [str, unicode]:
|
280
|
+
if type(name) not in [str, unicode]:
|
254
281
|
if _isDebug_: print("\x1b[31m[-] DataTypeError: the KEY name must be str or unicode, not {}\x1b[0m".format(type(name)))
|
255
282
|
return False
|
256
283
|
|
@@ -297,8 +324,12 @@ class JSON_DUPLICATE_KEYS:
|
|
297
324
|
return True
|
298
325
|
else:
|
299
326
|
if len(name.split(separator)) == 1:
|
300
|
-
self.getObject()[
|
301
|
-
|
327
|
+
if type(self.getObject()) in [dict, OrderedDict]:
|
328
|
+
self.getObject()[name] = value
|
329
|
+
return True
|
330
|
+
else:
|
331
|
+
if _isDebug_: print("\x1b[31m[-] DataTypeError: Cannot set name and value for a list object\x1b[0m")
|
332
|
+
return False
|
302
333
|
else:
|
303
334
|
if self.get(separator.join(name.split(separator)[:-1]), case_insensitive=case_insensitive, separator=separator, parse_index=parse_index)["value"] != "JSON_DUPLICATE_KEYS_ERROR":
|
304
335
|
Jget = self.get(separator.join(name.split(separator)[:-1]), case_insensitive=case_insensitive, separator=separator, parse_index=parse_index)
|
@@ -331,7 +362,7 @@ class JSON_DUPLICATE_KEYS:
|
|
331
362
|
# User input data type validation
|
332
363
|
if type(_isDebug_) != bool: _isDebug_ = False
|
333
364
|
|
334
|
-
if type(name) not in [str, unicode]:
|
365
|
+
if type(name) not in [str, unicode]:
|
335
366
|
if _isDebug_: print("\x1b[31m[-] DataTypeError: the KEY name must be str or unicode, not {}\x1b[0m".format(type(name)))
|
336
367
|
return False
|
337
368
|
|
@@ -376,7 +407,7 @@ class JSON_DUPLICATE_KEYS:
|
|
376
407
|
# User input data type validation
|
377
408
|
if type(_isDebug_) != bool: _isDebug_ = False
|
378
409
|
|
379
|
-
if type(name) not in [str, unicode]:
|
410
|
+
if type(name) not in [str, unicode]:
|
380
411
|
if _isDebug_: print("\x1b[31m[-] DataTypeError: the KEY name must be str or unicode, not {}\x1b[0m".format(type(name)))
|
381
412
|
return False
|
382
413
|
|
@@ -420,7 +451,7 @@ class JSON_DUPLICATE_KEYS:
|
|
420
451
|
# User input data type validation
|
421
452
|
if type(_isDebug_) != bool: _isDebug_ = False
|
422
453
|
|
423
|
-
if type(name) not in [str, unicode]:
|
454
|
+
if type(name) not in [str, unicode]:
|
424
455
|
if _isDebug_: print("\x1b[31m[-] DataTypeError: the KEY name must be str or unicode, not {}\x1b[0m".format(type(name)))
|
425
456
|
return False
|
426
457
|
|
@@ -463,10 +494,10 @@ class JSON_DUPLICATE_KEYS:
|
|
463
494
|
for k, v in JDKSObject.getObject().items():
|
464
495
|
if type(k) == str and type(name) == str:
|
465
496
|
if re.search(name, k):
|
466
|
-
newJDKSObject.set(k, v, separator="§§"+separator+"§§", parse_index="§§"+parse_index+"§§", ordered_dict=ordered_dict)
|
497
|
+
newJDKSObject.set(k, v, separator=u"§§"+separator+u"§§", parse_index=u"§§"+parse_index+u"§§", ordered_dict=ordered_dict)
|
467
498
|
else:
|
468
499
|
if name == k:
|
469
|
-
newJDKSObject.set(k, v, separator="§§"+separator+"§§", parse_index="§§"+parse_index+"§§", ordered_dict=ordered_dict)
|
500
|
+
newJDKSObject.set(k, v, separator=u"§§"+separator+u"§§", parse_index=u"§§"+parse_index+u"§§", ordered_dict=ordered_dict)
|
470
501
|
|
471
502
|
return newJDKSObject
|
472
503
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -485,10 +516,10 @@ class JSON_DUPLICATE_KEYS:
|
|
485
516
|
for k, v in JDKSObject.getObject().items():
|
486
517
|
if type(v) == str and type(value) == str:
|
487
518
|
if re.search(value, v):
|
488
|
-
newJDKSObject.set(k, v, separator="§§"+separator+"§§", parse_index="§§"+parse_index+"§§", ordered_dict=ordered_dict)
|
519
|
+
newJDKSObject.set(k, v, separator=u"§§"+separator+u"§§", parse_index=u"§§"+parse_index+u"§§", ordered_dict=ordered_dict)
|
489
520
|
else:
|
490
521
|
if value == v:
|
491
|
-
newJDKSObject.set(k, v, separator="§§"+separator+"§§", parse_index="§§"+parse_index+"§§", ordered_dict=ordered_dict)
|
522
|
+
newJDKSObject.set(k, v, separator=u"§§"+separator+u"§§", parse_index=u"§§"+parse_index+u"§§", ordered_dict=ordered_dict)
|
492
523
|
|
493
524
|
return newJDKSObject
|
494
525
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -515,7 +546,7 @@ class JSON_DUPLICATE_KEYS:
|
|
515
546
|
|
516
547
|
dupSign_end_escape_regex = re.escape(json.dumps({dupSign_end:""})[2:-6])
|
517
548
|
|
518
|
-
return re.sub('
|
549
|
+
return re.sub(dupSign_start_escape_regex+'_\\d+_'+dupSign_end_escape_regex+'":', '":', json.dumps(self.getObject(), skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, cls=cls, indent=indent, separators=separators, default=default, sort_keys=sort_keys))
|
519
550
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
520
551
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
521
552
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -528,8 +559,8 @@ class JSON_DUPLICATE_KEYS:
|
|
528
559
|
Jstr = self.dumps(dupSign_start=dupSign_start, dupSign_end=dupSign_end, _isDebug_=_isDebug_, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, cls=cls, indent=indent, separators=separators, default=default, sort_keys=sort_keys)
|
529
560
|
|
530
561
|
try:
|
531
|
-
Jfile = open(Jfilepath, "
|
532
|
-
Jfile.write(Jstr)
|
562
|
+
Jfile = open(Jfilepath, "wb")
|
563
|
+
Jfile.write(Jstr.encode("utf-8"))
|
533
564
|
Jfile.close()
|
534
565
|
except Exception as e:
|
535
566
|
if _isDebug_: print("\x1b[31m[-] ExceptionError: {}\x1b[0m".format(e))
|
@@ -571,7 +602,7 @@ class JSON_DUPLICATE_KEYS:
|
|
571
602
|
else:
|
572
603
|
for k,v in Jobj.items():
|
573
604
|
_Jobj = v
|
574
|
-
_key =
|
605
|
+
_key = key+separator+k if key != None else k
|
575
606
|
|
576
607
|
__convert_Jobj_to_Jflat(_Jobj, _key)
|
577
608
|
elif type(Jobj) == list:
|
@@ -580,7 +611,7 @@ class JSON_DUPLICATE_KEYS:
|
|
580
611
|
else:
|
581
612
|
for i,v in enumerate(Jobj):
|
582
613
|
_Jobj = v
|
583
|
-
_key =
|
614
|
+
_key = key+separator+parse_index+str(i)+parse_index if key != None else parse_index+str(i)+parse_index
|
584
615
|
|
585
616
|
__convert_Jobj_to_Jflat(_Jobj, _key)
|
586
617
|
else:
|
@@ -1,32 +1,51 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: json-duplicate-keys
|
3
|
-
Version: 2025.
|
3
|
+
Version: 2025.8.19
|
4
4
|
Summary: Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
Author-email: TP Cyber Security <tpcybersec2023@gmail.com>
|
6
|
+
License: MIT License
|
7
|
+
|
8
|
+
Copyright (c) 2022 TP Cyber Security
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
12
|
+
in the Software without restriction, including without limitation the rights
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
15
|
+
furnished to do so, subject to the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
18
|
+
copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
SOFTWARE.
|
27
|
+
Project-URL: Homepage, https://github.com/tpcybersec/json-duplicate-keys
|
9
28
|
Keywords: TPCyberSec,json,duplicate keys,json duplicate keys,flatten,unflatten
|
10
29
|
Classifier: Programming Language :: Python :: 3
|
11
|
-
Classifier: Programming Language :: Python :: 2
|
12
30
|
Classifier: Programming Language :: Python :: Implementation :: Jython
|
13
31
|
Description-Content-Type: text/markdown
|
14
32
|
License-File: LICENSE
|
15
33
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
<
|
20
|
-
<a href="https://github.com/
|
21
|
-
<a href="#"><img src="https://img.shields.io/github/downloads/
|
22
|
-
<a href="#"><img src="https://img.shields.io/github/stars/
|
23
|
-
<a href="#"><img src="https://img.shields.io/github/forks/
|
24
|
-
<a href="https://github.com/
|
25
|
-
<a href="https://github.com/
|
34
|
+
<div align="center">
|
35
|
+
<h1>JSON Duplicate Keys - PyPI</h1>
|
36
|
+
<i>Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys</i>
|
37
|
+
<br><br>
|
38
|
+
<a href="https://github.com/tpcybersec/json-duplicate-keys/releases/"><img src="https://img.shields.io/github/release/tpcybersec/json-duplicate-keys" height=30></a>
|
39
|
+
<a href="#"><img src="https://img.shields.io/github/downloads/tpcybersec/json-duplicate-keys/total" height=30></a>
|
40
|
+
<a href="#"><img src="https://img.shields.io/github/stars/tpcybersec/json-duplicate-keys" height=30></a>
|
41
|
+
<a href="#"><img src="https://img.shields.io/github/forks/tpcybersec/json-duplicate-keys" height=30></a>
|
42
|
+
<a href="https://github.com/tpcybersec/json-duplicate-keys/issues?q=is%3Aopen+is%3Aissue"><img src="https://img.shields.io/github/issues/tpcybersec/json-duplicate-keys" height=30></a>
|
43
|
+
<a href="https://github.com/tpcybersec/json-duplicate-keys/issues?q=is%3Aissue+is%3Aclosed"><img src="https://img.shields.io/github/issues-closed/tpcybersec/json-duplicate-keys" height=30></a>
|
26
44
|
<br>
|
27
45
|
<a href="#"><img src="https://img.shields.io/pypi/v/json-duplicate-keys" height=30></a>
|
46
|
+
<a href="#"><img src="https://img.shields.io/pypi/pyversions/json-duplicate-keys" height=30></a>
|
28
47
|
<a href="#"><img src="https://img.shields.io/pypi/dm/json-duplicate-keys" height=30></a>
|
29
|
-
</
|
48
|
+
</div>
|
30
49
|
|
31
50
|
## Installation
|
32
51
|
#### From PyPI:
|
@@ -35,18 +54,18 @@ pip install json-duplicate-keys
|
|
35
54
|
```
|
36
55
|
#### From Source:
|
37
56
|
```console
|
38
|
-
git clone https://github.com/
|
57
|
+
git clone https://github.com/tpcybersec/json-duplicate-keys.git --branch <Branch/Tag>
|
39
58
|
cd json-duplicate-keys
|
40
|
-
python
|
41
|
-
python
|
59
|
+
python -m build
|
60
|
+
python -m pip install dist/json_duplicate_keys-<version>-py3-none-any.whl
|
42
61
|
```
|
43
62
|
|
44
63
|
## Basic Usage
|
45
64
|
### normalize_key(`name`, `dupSign_start`="{{{", `dupSign_end`="}}}", `_isDebug_`=False)
|
46
65
|
_Normalize Key name_
|
47
66
|
- `name`: key name
|
48
|
-
- `dupSign_start`:
|
49
|
-
- `dupSign_end`:
|
67
|
+
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
68
|
+
- `dupSign_end`: End symbol for marking duplicates (default: `}}}`)
|
50
69
|
- `_isDebug_`: Show/ Hide debug error messages
|
51
70
|
```python
|
52
71
|
import json_duplicate_keys as jdks
|
@@ -59,8 +78,8 @@ print(jdks.normalize_key("version{{{_2_}}}"))
|
|
59
78
|
### loads(`Jstr`, `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `skipDuplicated`=False, `_isDebug_`=False)
|
60
79
|
_Deserialize a JSON format string to a class `JSON_DUPLICATE_KEYS`_
|
61
80
|
- `Jstr`: a JSON format string
|
62
|
-
- `dupSign_start`:
|
63
|
-
- `dupSign_end`:
|
81
|
+
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
82
|
+
- `dupSign_end`: End symbol for marking duplicates (default: `}}}`)
|
64
83
|
- `ordered_dict`: preserves the order in which the Keys are inserted
|
65
84
|
- `skipDuplicated`: Skip loading duplicate keys to improve execution performance
|
66
85
|
- `_isDebug_`: Show/ Hide debug error messages
|
@@ -79,8 +98,8 @@ print(JDKSObject)
|
|
79
98
|
### load(`Jfilepath`, `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `skipDuplicated`=False, `_isDebug_`=False)
|
80
99
|
_Deserialize a JSON format string from a file to a class `JSON_DUPLICATE_KEYS`_
|
81
100
|
- `Jfilepath`: The path to the file containing the JSON format string
|
82
|
-
- `dupSign_start`:
|
83
|
-
- `dupSign_end`:
|
101
|
+
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
102
|
+
- `dupSign_end`: End symbol for marking duplicates (default: `}}}`)
|
84
103
|
- `ordered_dict`: preserves the order in which the Keys are inserted
|
85
104
|
- `skipDuplicated`: Skip loading duplicate keys to improve execution performance
|
86
105
|
- `_isDebug_`: Show/ Hide debug error messages
|
@@ -116,8 +135,8 @@ print(JDKSObject.getObject())
|
|
116
135
|
_Get value in the JSON object by `name`_
|
117
136
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
118
137
|
- `case_insensitive`: the key name case (in)sensitive
|
119
|
-
- `separator`:
|
120
|
-
- `parse_index`:
|
138
|
+
- `separator`: Separator for flatten keys (default: `||`)
|
139
|
+
- `parse_index`: Symbol for index parsing (default: `$`)
|
121
140
|
- `_isDebug_`: Show/ Hide debug error messages
|
122
141
|
```python
|
123
142
|
import json_duplicate_keys as jdks
|
@@ -142,10 +161,10 @@ _Set a new `name` and `value` for the JSON object_
|
|
142
161
|
- `name`: new key name for the JSON object. Supported flat key name format
|
143
162
|
- `value`: value for key `name`
|
144
163
|
- `case_insensitive`: the key name case (in)sensitive
|
145
|
-
- `separator`:
|
146
|
-
- `parse_index`:
|
147
|
-
- `dupSign_start`:
|
148
|
-
- `dupSign_end`:
|
164
|
+
- `separator`: Separator for flatten keys (default: `||`)
|
165
|
+
- `parse_index`: Symbol for index parsing (default: `$`)
|
166
|
+
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
167
|
+
- `dupSign_end`: End symbol for marking duplicates (default: `}}}`)
|
149
168
|
- `ordered_dict`: preserves the order in which the Keys are inserted
|
150
169
|
- `_isDebug_`: Show/Hide debug error messages
|
151
170
|
```python
|
@@ -180,25 +199,6 @@ print(JDKSObject.getObject())
|
|
180
199
|
JDKSObject.set("snapshot||author", "truocphan")
|
181
200
|
print(JDKSObject.getObject())
|
182
201
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan'}}
|
183
|
-
|
184
|
-
|
185
|
-
Jstr = '[]'
|
186
|
-
JDKSObject = jdks.loads(Jstr)
|
187
|
-
|
188
|
-
print(JDKSObject.getObject())
|
189
|
-
# OUTPUT: []
|
190
|
-
|
191
|
-
JDKSObject.set("author", "truocphan")
|
192
|
-
print(JDKSObject.getObject())
|
193
|
-
# OUTPUT: [{'author': 'truocphan'}]
|
194
|
-
|
195
|
-
JDKSObject.set("release", [])
|
196
|
-
print(JDKSObject.getObject())
|
197
|
-
# OUTPUT: [{'author': 'truocphan'}, {'release': []}]
|
198
|
-
|
199
|
-
JDKSObject.set("$1$||release||", {"version": "latest"})
|
200
|
-
print(JDKSObject.getObject())
|
201
|
-
# OUTPUT: [{'author': 'truocphan'}, {'release': [{'version': 'latest'}]}]
|
202
202
|
```
|
203
203
|
---
|
204
204
|
|
@@ -208,10 +208,10 @@ _Insert `value` at `position` in value list of `name`_
|
|
208
208
|
- `value`: new value for key `name`
|
209
209
|
- `position`: position of the `value` to insert (default insert at the last position of the list)
|
210
210
|
- `case_insensitive`: the key name case (in)sensitive
|
211
|
-
- `separator`:
|
212
|
-
- `parse_index`:
|
213
|
-
- `dupSign_start`:
|
214
|
-
- `dupSign_end`:
|
211
|
+
- `separator`: Separator for flatten keys (default: `||`)
|
212
|
+
- `parse_index`: Symbol for index parsing (default: `$`)
|
213
|
+
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
214
|
+
- `dupSign_end`: End symbol for marking duplicates (default: `}}}`)
|
215
215
|
- `_isDebug_`: Show/ Hide debug error messages
|
216
216
|
```python
|
217
217
|
import json_duplicate_keys as jdks
|
@@ -237,10 +237,10 @@ _Update new `value` for existing `name` or Set a new `name` in the JSON object_
|
|
237
237
|
- `value`: new value for key `name`
|
238
238
|
- `case_insensitive`: the key name case (in)sensitive
|
239
239
|
- `allow_new_key`: allows to create a new key name if the key name does not exist
|
240
|
-
- `separator`:
|
241
|
-
- `parse_index`:
|
242
|
-
- `dupSign_start`:
|
243
|
-
- `dupSign_end`:
|
240
|
+
- `separator`: Separator for flatten keys (default: `||`)
|
241
|
+
- `parse_index`: Symbol for index parsing (default: `$`)
|
242
|
+
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
243
|
+
- `dupSign_end`: End symbol for marking duplicates (default: `}}}`)
|
244
244
|
- `ordered_dict`: preserves the order in which the Keys are inserted
|
245
245
|
- `_isDebug_`: Show/ Hide debug error messages
|
246
246
|
```python
|
@@ -265,8 +265,8 @@ print(JDKSObject.getObject())
|
|
265
265
|
_Delete a key-value pair in a JSON object by key `name`_
|
266
266
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
267
267
|
- `case_insensitive`: the key name case (in)sensitive
|
268
|
-
- `separator`:
|
269
|
-
- `parse_index`:
|
268
|
+
- `separator`: Separator for flatten keys (default: `||`)
|
269
|
+
- `parse_index`: Symbol for index parsing (default: `$`)
|
270
270
|
- `_isDebug_`: Show/ Hide debug error messages
|
271
271
|
```python
|
272
272
|
import json_duplicate_keys as jdks
|
@@ -288,10 +288,10 @@ print(JDKSObject.getObject())
|
|
288
288
|
---
|
289
289
|
|
290
290
|
### JSON_DUPLICATE_KEYS.filter_keys(`name`, `separator`="||", `parse_index`="$", `ordered_dict`=False)
|
291
|
-
|
291
|
+
_Return a `JSON_DUPLICATE_KEYS` object with keys matching a pattern_
|
292
292
|
- `name`:
|
293
|
-
- `separator`:
|
294
|
-
- `parse_index`:
|
293
|
+
- `separator`: Separator for flatten keys (default: `||`)
|
294
|
+
- `parse_index`: Symbol for index parsing (default: `$`)
|
295
295
|
- `ordered_dict`: preserves the order in which the Keys are inserted
|
296
296
|
```python
|
297
297
|
import json_duplicate_keys as jdks
|
@@ -309,10 +309,10 @@ print(JDKSObject.dumps())
|
|
309
309
|
---
|
310
310
|
|
311
311
|
### JSON_DUPLICATE_KEYS.filter_values(`value`, `separator`="||", `parse_index`="$", `ordered_dict`=False)
|
312
|
-
|
312
|
+
_Return a `JSON_DUPLICATE_KEYS` object with values matching a pattern_
|
313
313
|
- `value`:
|
314
|
-
- `separator`:
|
315
|
-
- `parse_index`:
|
314
|
+
- `separator`: Separator for flatten keys (default: `||`)
|
315
|
+
- `parse_index`: Symbol for index parsing (default: `$`)
|
316
316
|
- `ordered_dict`: preserves the order in which the Keys are inserted
|
317
317
|
```python
|
318
318
|
import json_duplicate_keys as jdks
|
@@ -331,8 +331,8 @@ print(JDKSObject.dumps())
|
|
331
331
|
|
332
332
|
### JSON_DUPLICATE_KEYS.dumps(`dupSign_start`="{{{", `dupSign_end`="}}}", `_isDebug_`=False, `skipkeys`=False, `ensure_ascii`=True, `check_circular`=True, `allow_nan`=True, `cls`=None, `indent`=None, `separators`=None, `default`=None, `sort_keys`=False)
|
333
333
|
_Serialize a JSON object to a JSON format string_
|
334
|
-
- `dupSign_start`:
|
335
|
-
- `dupSign_end`:
|
334
|
+
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
335
|
+
- `dupSign_end`: End symbol for marking duplicates (default: `}}}`)
|
336
336
|
- `_isDebug_`: Show/ Hide debug error messages
|
337
337
|
- For remaining arguments, please refer to [json.dump()](https://docs.python.org/3/library/json.html#json.dump)
|
338
338
|
```python
|
@@ -357,8 +357,8 @@ print(JDKSObject.dumps())
|
|
357
357
|
### JSON_DUPLICATE_KEYS.dump(`Jfilepath`, `dupSign_start`="{{{", `dupSign_end`="}}}", `_isDebug_`=False, `skipkeys`=False, `ensure_ascii`=True, `check_circular`=True, `allow_nan`=True, `cls`=None, `indent`=None, `separators`=None, `default`=None, `sort_keys`=False)
|
358
358
|
_Serialize a JSON object to a JSON format string and write to a file_
|
359
359
|
- `Jfilepath`: the path to the file to save the JSON format string
|
360
|
-
- `dupSign_start`:
|
361
|
-
- `dupSign_end`:
|
360
|
+
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
361
|
+
- `dupSign_end`: End symbol for marking duplicates (default: `}}}`)
|
362
362
|
- `_isDebug_`: Show/ Hide debug error messages
|
363
363
|
- For remaining arguments, please refer to [json.dump()](https://docs.python.org/3/library/json.html#json.dump)
|
364
364
|
```python
|
@@ -386,8 +386,8 @@ print(JDKSObject_load.getObject())
|
|
386
386
|
|
387
387
|
### JSON_DUPLICATE_KEYS.flatten(`separator`="||", `parse_index`="$", `ordered_dict`=False, `_isDebug_`=False)
|
388
388
|
_Flatten a JSON object to a single key-value pairs_
|
389
|
-
- `separator`:
|
390
|
-
- `parse_index`:
|
389
|
+
- `separator`: Separator for flatten keys (default: `||`)
|
390
|
+
- `parse_index`: Symbol for index parsing (default: `$`)
|
391
391
|
- `ordered_dict`: preserves the order in which the Keys are inserted
|
392
392
|
- `_isDebug_`: Show/ Hide debug error messages
|
393
393
|
```python
|
@@ -409,8 +409,8 @@ print(JDKSObject.getObject())
|
|
409
409
|
|
410
410
|
### JSON_DUPLICATE_KEYS.unflatten(`separator`="||", `parse_index`="$", `ordered_dict`=False, `_isDebug_`=False)
|
411
411
|
_Unflatten a flattened JSON object back to a JSON object_
|
412
|
-
- `separator`:
|
413
|
-
- `parse_index`:
|
412
|
+
- `separator`: Separator for flatten keys (default: `||`)
|
413
|
+
- `parse_index`: Symbol for index parsing (default: `$`)
|
414
414
|
- `ordered_dict`: preserves the order in which the Keys are inserted
|
415
415
|
- `_isDebug_`: Show/ Hide debug error messages
|
416
416
|
```python
|
@@ -431,25 +431,34 @@ print(JDKSObject.getObject())
|
|
431
431
|
---
|
432
432
|
|
433
433
|
## CHANGELOG
|
434
|
-
#### [json-duplicate-keys v2025.
|
434
|
+
#### [json-duplicate-keys v2025.8.19](https://github.com/tpcybersec/json-duplicate-keys/tree/2025.8.19)
|
435
|
+
- [**Updated**] Add an exception when loading a non-existent file
|
436
|
+
- [**Updated**] Dump Unicode characters to a file
|
437
|
+
|
438
|
+
#### [json-duplicate-keys v2025.7.1](https://github.com/tpcybersec/json-duplicate-keys/tree/2025.7.1)
|
439
|
+
- [**Updated**] Fixed some issues when loading JSON strings with `skipDuplicated` option
|
440
|
+
- [**Updated**] Allow loading of JSON data in byte string format
|
441
|
+
- [**Updated**] Issue with getting and setting an empty list
|
442
|
+
|
443
|
+
#### [json-duplicate-keys v2025.6.6](https://github.com/tpcybersec/json-duplicate-keys/tree/2025.6.6)
|
435
444
|
- [**Updated**] Added `skipDuplicated` parameter to `load` and `loads` functions to improve performance when parsing large JSON strings by skipping duplicate keys.
|
436
445
|
|
437
|
-
#### [json-duplicate-keys v2024.12.12](https://github.com/
|
446
|
+
#### [json-duplicate-keys v2024.12.12](https://github.com/tpcybersec/json-duplicate-keys/tree/2024.12.12)
|
438
447
|
- **New**: _insert_ function
|
439
448
|
|
440
|
-
#### [json-duplicate-keys v2024.11.28](https://github.com/
|
449
|
+
#### [json-duplicate-keys v2024.11.28](https://github.com/tpcybersec/json-duplicate-keys/tree/2024.11.28)
|
441
450
|
- **Fixed**: Add subkey name to empty dict of existing key name
|
442
451
|
|
443
|
-
#### [json-duplicate-keys v2024.11.19](https://github.com/
|
452
|
+
#### [json-duplicate-keys v2024.11.19](https://github.com/tpcybersec/json-duplicate-keys/tree/2024.11.19)
|
444
453
|
- **Updated**: Allows getting (`JSON_DUPLICATE_KEYS.get`), setting (`JSON_DUPLICATE_KEYS.set`), updating (`JSON_DUPLICATE_KEYS.update`), deleting (`JSON_DUPLICATE_KEYS.delete`) JSON_DUPLICATE_KEYS objects with case-insensitive key names
|
445
454
|
|
446
|
-
#### [json-duplicate-keys v2024.7.17](https://github.com/
|
447
|
-
- **Fixed**: issue
|
455
|
+
#### [json-duplicate-keys v2024.7.17](https://github.com/tpcybersec/json-duplicate-keys/tree/2024.7.17)
|
456
|
+
- **Fixed**: issue #3 break the set function when the key's value is empty. Thanks [ptth222](https://github.com/ptth222) for reporting this issue.
|
448
457
|
|
449
|
-
#### [json-duplicate-keys v2024.4.20](https://github.com/
|
458
|
+
#### [json-duplicate-keys v2024.4.20](https://github.com/tpcybersec/json-duplicate-keys/tree/2024.4.20)
|
450
459
|
- **New**: _filter_values_
|
451
460
|
- **Updated**: _filter_keys_
|
452
461
|
|
453
|
-
#### [json-duplicate-keys v2024.3.24](https://github.com/
|
462
|
+
#### [json-duplicate-keys v2024.3.24](https://github.com/tpcybersec/json-duplicate-keys/tree/2024.3.24)
|
454
463
|
- **Updated**: _normalize_key_, _loads_, _get_, _set_, _update_, _delete_
|
455
464
|
---
|
@@ -0,0 +1,6 @@
|
|
1
|
+
json_duplicate_keys/__init__.py,sha256=j-jA6opC7If-__TFmt86AKtw9h7msoSUkUPpfUZ3YP4,30097
|
2
|
+
json_duplicate_keys-2025.8.19.dist-info/LICENSE,sha256=_hudSGMciUc27wGR8EMKfeb3atJRlf6rbhJtLnel-nc,1093
|
3
|
+
json_duplicate_keys-2025.8.19.dist-info/METADATA,sha256=g8CkjaHAODINJs7QvcCyilmGzbQnLKVWX6r9G1guWHY,24279
|
4
|
+
json_duplicate_keys-2025.8.19.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
5
|
+
json_duplicate_keys-2025.8.19.dist-info/top_level.txt,sha256=vdsGrPLVS_l-BFL1i4hCJBY5_-gq4Cwp-11yegA750Y,20
|
6
|
+
json_duplicate_keys-2025.8.19.dist-info/RECORD,,
|
@@ -1,6 +0,0 @@
|
|
1
|
-
json_duplicate_keys/__init__.py,sha256=vxG-My8fNJiG90q6flLf0_vmOJVyWjjm96ESP1grFn8,29024
|
2
|
-
json_duplicate_keys-2025.6.6.dist-info/LICENSE,sha256=_hudSGMciUc27wGR8EMKfeb3atJRlf6rbhJtLnel-nc,1093
|
3
|
-
json_duplicate_keys-2025.6.6.dist-info/METADATA,sha256=BELit1CyMqG530hTLEkU43h1sxcx7r0JE7o0Vv34rzs,21194
|
4
|
-
json_duplicate_keys-2025.6.6.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
5
|
-
json_duplicate_keys-2025.6.6.dist-info/top_level.txt,sha256=vdsGrPLVS_l-BFL1i4hCJBY5_-gq4Cwp-11yegA750Y,20
|
6
|
-
json_duplicate_keys-2025.6.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{json_duplicate_keys-2025.6.6.dist-info → json_duplicate_keys-2025.8.19.dist-info}/top_level.txt
RENAMED
File without changes
|