json-duplicate-keys 2025.7.1__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 +28 -26
- {json_duplicate_keys-2025.7.1.dist-info → json_duplicate_keys-2025.8.19.dist-info}/METADATA +52 -29
- json_duplicate_keys-2025.8.19.dist-info/RECORD +6 -0
- json_duplicate_keys-2025.7.1.dist-info/RECORD +0 -6
- {json_duplicate_keys-2025.7.1.dist-info → json_duplicate_keys-2025.8.19.dist-info}/LICENSE +0 -0
- {json_duplicate_keys-2025.7.1.dist-info → json_duplicate_keys-2025.8.19.dist-info}/WHEEL +0 -0
- {json_duplicate_keys-2025.7.1.dist-info → json_duplicate_keys-2025.8.19.dist-info}/top_level.txt +0 -0
json_duplicate_keys/__init__.py
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
json_duplicate_keys_VERSION = "2025.7.1"
|
3
|
-
|
4
2
|
try:
|
5
|
-
unicode
|
3
|
+
unicode
|
6
4
|
except NameError:
|
7
|
-
unicode = str
|
5
|
+
unicode = str
|
8
6
|
|
9
7
|
from collections import OrderedDict
|
10
8
|
import json, re, copy
|
@@ -24,7 +22,7 @@ def normalize_key(name, dupSign_start="{{{", dupSign_end="}}}", _isDebug_=False)
|
|
24
22
|
|
25
23
|
if type(dupSign_end) not in [str, unicode]: dupSign_end = "}}}"
|
26
24
|
|
27
|
-
return re.sub('
|
25
|
+
return re.sub(re.escape(dupSign_start)+'_\\d+_'+re.escape(dupSign_end)+'$', "", name)
|
28
26
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
29
27
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
30
28
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -118,13 +116,13 @@ def loads(Jstr, dupSign_start="{{{", dupSign_end="}}}", ordered_dict=False, skip
|
|
118
116
|
Jstr = re.sub(r'"([^"]*)"[\s\t\r\n]*([,\]}])'.encode(), '\x04\x05\\1\x04\x05\\2'.encode(), Jstr)
|
119
117
|
|
120
118
|
|
121
|
-
Jstr = re.sub(r'"([^"]+)"[\s\t\r\n]*:'.encode(), r'"\1
|
119
|
+
Jstr = re.sub(r'"([^"]+)"[\s\t\r\n]*:'.encode(), (r'"\1'+dupSign_start_escape+'_dupSign_'+dupSign_end_escape+'":').encode(), Jstr)
|
122
120
|
|
123
|
-
Jstr = re.sub(r'""[\s\t\r\n]*:'.encode(), '"
|
121
|
+
Jstr = re.sub(r'""[\s\t\r\n]*:'.encode(), ('"'+dupSign_start_escape+'_dupSign_'+dupSign_end_escape+'":').encode(), Jstr)
|
124
122
|
|
125
123
|
i = 0
|
126
|
-
while re.search(r'
|
127
|
-
Jstr = re.sub(r'
|
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)
|
128
126
|
i += 1
|
129
127
|
|
130
128
|
Jstr = re.sub('\x00\x01'.encode(), r'\\\\'.encode(), Jstr)
|
@@ -135,13 +133,13 @@ def loads(Jstr, dupSign_start="{{{", dupSign_end="}}}", ordered_dict=False, skip
|
|
135
133
|
Jstr = re.sub(r'\\"', '\x02\x03', Jstr)
|
136
134
|
Jstr = re.sub(r'"([^"]*)"[\s\t\r\n]*([,\]}])', '\x04\x05\\1\x04\x05\\2', Jstr)
|
137
135
|
|
138
|
-
Jstr = re.sub(r'"([^"]+)"[\s\t\r\n]*:', r'"\1
|
136
|
+
Jstr = re.sub(r'"([^"]+)"[\s\t\r\n]*:', r'"\1'+dupSign_start_escape+'_dupSign_'+dupSign_end_escape+'":', Jstr)
|
139
137
|
|
140
|
-
Jstr = re.sub(r'""[\s\t\r\n]*:', '"
|
138
|
+
Jstr = re.sub(r'""[\s\t\r\n]*:', '"'+dupSign_start_escape+'_dupSign_'+dupSign_end_escape+'":', Jstr)
|
141
139
|
|
142
140
|
i = 0
|
143
|
-
while re.search(r'
|
144
|
-
Jstr = re.sub(r'
|
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)
|
145
143
|
i += 1
|
146
144
|
|
147
145
|
Jstr = re.sub('\x00\x01', r'\\\\', Jstr)
|
@@ -178,11 +176,15 @@ def loads(Jstr, dupSign_start="{{{", dupSign_end="}}}", ordered_dict=False, skip
|
|
178
176
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
179
177
|
def load(Jfilepath, dupSign_start="{{{", dupSign_end="}}}", ordered_dict=False, skipDuplicated=False, _isDebug_=False):
|
180
178
|
try:
|
181
|
-
|
182
|
-
|
183
|
-
|
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()
|
184
183
|
|
185
|
-
|
184
|
+
return loads(Jstr, dupSign_start=dupSign_start, dupSign_end=dupSign_end, ordered_dict=ordered_dict, skipDuplicated=skipDuplicated, _isDebug_=_isDebug_)
|
185
|
+
except Exception as e:
|
186
|
+
if _isDebug_: print("\x1b[31m[-] ExceptionError: {}\x1b[0m".format(e))
|
187
|
+
return False
|
186
188
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
187
189
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
188
190
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -492,10 +494,10 @@ class JSON_DUPLICATE_KEYS:
|
|
492
494
|
for k, v in JDKSObject.getObject().items():
|
493
495
|
if type(k) == str and type(name) == str:
|
494
496
|
if re.search(name, k):
|
495
|
-
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)
|
496
498
|
else:
|
497
499
|
if name == k:
|
498
|
-
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)
|
499
501
|
|
500
502
|
return newJDKSObject
|
501
503
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -514,10 +516,10 @@ class JSON_DUPLICATE_KEYS:
|
|
514
516
|
for k, v in JDKSObject.getObject().items():
|
515
517
|
if type(v) == str and type(value) == str:
|
516
518
|
if re.search(value, v):
|
517
|
-
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)
|
518
520
|
else:
|
519
521
|
if value == v:
|
520
|
-
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)
|
521
523
|
|
522
524
|
return newJDKSObject
|
523
525
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -544,7 +546,7 @@ class JSON_DUPLICATE_KEYS:
|
|
544
546
|
|
545
547
|
dupSign_end_escape_regex = re.escape(json.dumps({dupSign_end:""})[2:-6])
|
546
548
|
|
547
|
-
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))
|
548
550
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
549
551
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
550
552
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -557,8 +559,8 @@ class JSON_DUPLICATE_KEYS:
|
|
557
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)
|
558
560
|
|
559
561
|
try:
|
560
|
-
Jfile = open(Jfilepath, "
|
561
|
-
Jfile.write(Jstr)
|
562
|
+
Jfile = open(Jfilepath, "wb")
|
563
|
+
Jfile.write(Jstr.encode("utf-8"))
|
562
564
|
Jfile.close()
|
563
565
|
except Exception as e:
|
564
566
|
if _isDebug_: print("\x1b[31m[-] ExceptionError: {}\x1b[0m".format(e))
|
@@ -600,7 +602,7 @@ class JSON_DUPLICATE_KEYS:
|
|
600
602
|
else:
|
601
603
|
for k,v in Jobj.items():
|
602
604
|
_Jobj = v
|
603
|
-
_key =
|
605
|
+
_key = key+separator+k if key != None else k
|
604
606
|
|
605
607
|
__convert_Jobj_to_Jflat(_Jobj, _key)
|
606
608
|
elif type(Jobj) == list:
|
@@ -609,7 +611,7 @@ class JSON_DUPLICATE_KEYS:
|
|
609
611
|
else:
|
610
612
|
for i,v in enumerate(Jobj):
|
611
613
|
_Jobj = v
|
612
|
-
_key =
|
614
|
+
_key = key+separator+parse_index+str(i)+parse_index if key != None else parse_index+str(i)+parse_index
|
613
615
|
|
614
616
|
__convert_Jobj_to_Jflat(_Jobj, _key)
|
615
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,10 +54,10 @@ 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
|
@@ -412,30 +431,34 @@ print(JDKSObject.getObject())
|
|
412
431
|
---
|
413
432
|
|
414
433
|
## CHANGELOG
|
415
|
-
#### [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)
|
416
439
|
- [**Updated**] Fixed some issues when loading JSON strings with `skipDuplicated` option
|
417
440
|
- [**Updated**] Allow loading of JSON data in byte string format
|
418
441
|
- [**Updated**] Issue with getting and setting an empty list
|
419
442
|
|
420
|
-
#### [json-duplicate-keys v2025.6.6](https://github.com/
|
443
|
+
#### [json-duplicate-keys v2025.6.6](https://github.com/tpcybersec/json-duplicate-keys/tree/2025.6.6)
|
421
444
|
- [**Updated**] Added `skipDuplicated` parameter to `load` and `loads` functions to improve performance when parsing large JSON strings by skipping duplicate keys.
|
422
445
|
|
423
|
-
#### [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)
|
424
447
|
- **New**: _insert_ function
|
425
448
|
|
426
|
-
#### [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)
|
427
450
|
- **Fixed**: Add subkey name to empty dict of existing key name
|
428
451
|
|
429
|
-
#### [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)
|
430
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
|
431
454
|
|
432
|
-
#### [json-duplicate-keys v2024.7.17](https://github.com/
|
433
|
-
- **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.
|
434
457
|
|
435
|
-
#### [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)
|
436
459
|
- **New**: _filter_values_
|
437
460
|
- **Updated**: _filter_keys_
|
438
461
|
|
439
|
-
#### [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)
|
440
463
|
- **Updated**: _normalize_key_, _loads_, _get_, _set_, _update_, _delete_
|
441
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=ClhnGD0jyYwBRsOs9zHevi6WWGtnMEOjczwkUC0BMv8,30753
|
2
|
-
json_duplicate_keys-2025.7.1.dist-info/LICENSE,sha256=_hudSGMciUc27wGR8EMKfeb3atJRlf6rbhJtLnel-nc,1093
|
3
|
-
json_duplicate_keys-2025.7.1.dist-info/METADATA,sha256=wvDz2_ia5VuzhLvNiVZnrI7YJEVfjGVwy07RGb2-dQk,22717
|
4
|
-
json_duplicate_keys-2025.7.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
5
|
-
json_duplicate_keys-2025.7.1.dist-info/top_level.txt,sha256=vdsGrPLVS_l-BFL1i4hCJBY5_-gq4Cwp-11yegA750Y,20
|
6
|
-
json_duplicate_keys-2025.7.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{json_duplicate_keys-2025.7.1.dist-info → json_duplicate_keys-2025.8.19.dist-info}/top_level.txt
RENAMED
File without changes
|