json-duplicate-keys 2025.7.1__tar.gz → 2025.9.9__tar.gz
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-2025.7.1 → json_duplicate_keys-2025.9.9}/PKG-INFO +68 -49
- json_duplicate_keys-2025.7.1/json_duplicate_keys.egg-info/PKG-INFO → json_duplicate_keys-2025.9.9/README.md +66 -60
- {json_duplicate_keys-2025.7.1 → json_duplicate_keys-2025.9.9}/json_duplicate_keys/__init__.py +36 -28
- json_duplicate_keys-2025.7.1/README.md → json_duplicate_keys-2025.9.9/json_duplicate_keys.egg-info/PKG-INFO +79 -45
- {json_duplicate_keys-2025.7.1 → json_duplicate_keys-2025.9.9}/json_duplicate_keys.egg-info/SOURCES.txt +1 -3
- json_duplicate_keys-2025.9.9/pyproject.toml +25 -0
- json_duplicate_keys-2025.7.1/MANIFEST.in +0 -1
- json_duplicate_keys-2025.7.1/requirements.txt +0 -0
- json_duplicate_keys-2025.7.1/setup.py +0 -22
- {json_duplicate_keys-2025.7.1 → json_duplicate_keys-2025.9.9}/LICENSE +0 -0
- {json_duplicate_keys-2025.7.1 → json_duplicate_keys-2025.9.9}/json_duplicate_keys.egg-info/dependency_links.txt +0 -0
- {json_duplicate_keys-2025.7.1 → json_duplicate_keys-2025.9.9}/json_duplicate_keys.egg-info/top_level.txt +0 -0
- {json_duplicate_keys-2025.7.1 → json_duplicate_keys-2025.9.9}/setup.cfg +0 -0
@@ -1,47 +1,48 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: json-duplicate-keys
|
3
|
-
Version: 2025.
|
3
|
+
Version: 2025.9.9
|
4
4
|
Summary: Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys
|
5
|
-
|
6
|
-
Author: TP Cyber Security
|
7
|
-
Author-email: tpcybersec2023@gmail.com
|
5
|
+
Author-email: TP Cyber Security <tpcybersec2023@gmail.com>
|
8
6
|
License: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/TPCyberSec/json-duplicate-keys
|
9
8
|
Keywords: TPCyberSec,json,duplicate keys,json duplicate keys,flatten,unflatten
|
10
9
|
Classifier: Programming Language :: Python :: 3
|
11
|
-
Classifier: Programming Language :: Python :: 2
|
12
10
|
Classifier: Programming Language :: Python :: Implementation :: Jython
|
13
11
|
Description-Content-Type: text/markdown
|
14
12
|
License-File: LICENSE
|
15
13
|
|
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/
|
14
|
+
<div align="center">
|
15
|
+
<h1>JSON Duplicate Keys - PyPI</h1>
|
16
|
+
<i>Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys</i>
|
17
|
+
<br><br>
|
18
|
+
<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>
|
19
|
+
<a href="#"><img src="https://img.shields.io/github/downloads/TPCyberSec/json-duplicate-keys/total" height=30></a>
|
20
|
+
<a href="#"><img src="https://img.shields.io/github/stars/TPCyberSec/json-duplicate-keys" height=30></a>
|
21
|
+
<a href="#"><img src="https://img.shields.io/github/forks/TPCyberSec/json-duplicate-keys" height=30></a>
|
22
|
+
<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>
|
23
|
+
<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
24
|
<br>
|
27
25
|
<a href="#"><img src="https://img.shields.io/pypi/v/json-duplicate-keys" height=30></a>
|
26
|
+
<a href="#"><img src="https://img.shields.io/pypi/pyversions/json-duplicate-keys" height=30></a>
|
28
27
|
<a href="#"><img src="https://img.shields.io/pypi/dm/json-duplicate-keys" height=30></a>
|
29
|
-
</
|
28
|
+
</div>
|
30
29
|
|
31
|
-
|
30
|
+
---
|
31
|
+
# 🛠️ Installation
|
32
32
|
#### From PyPI:
|
33
33
|
```console
|
34
34
|
pip install json-duplicate-keys
|
35
35
|
```
|
36
36
|
#### From Source:
|
37
37
|
```console
|
38
|
-
git clone https://github.com/
|
38
|
+
git clone https://github.com/TPCyberSec/json-duplicate-keys.git --branch <Branch/Tag>
|
39
39
|
cd json-duplicate-keys
|
40
|
-
python
|
41
|
-
python
|
40
|
+
python -m build
|
41
|
+
python -m pip install dist/json_duplicate_keys-<version>-py3-none-any.whl
|
42
42
|
```
|
43
43
|
|
44
|
-
|
44
|
+
---
|
45
|
+
# 📘 Basic Usage
|
45
46
|
### normalize_key(`name`, `dupSign_start`="{{{", `dupSign_end`="}}}", `_isDebug_`=False)
|
46
47
|
_Normalize Key name_
|
47
48
|
- `name`: key name
|
@@ -54,8 +55,8 @@ import json_duplicate_keys as jdks
|
|
54
55
|
print(jdks.normalize_key("version{{{_2_}}}"))
|
55
56
|
# OUTPUT: version
|
56
57
|
```
|
57
|
-
---
|
58
58
|
|
59
|
+
---
|
59
60
|
### loads(`Jstr`, `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `skipDuplicated`=False, `_isDebug_`=False)
|
60
61
|
_Deserialize a JSON format string to a class `JSON_DUPLICATE_KEYS`_
|
61
62
|
- `Jstr`: a JSON format string
|
@@ -74,8 +75,8 @@ JDKSObject = jdks.loads(Jstr)
|
|
74
75
|
print(JDKSObject)
|
75
76
|
# OUTPUT: <json_duplicate_keys.JSON_DUPLICATE_KEYS object at 0x00000270AE987940>
|
76
77
|
```
|
77
|
-
---
|
78
78
|
|
79
|
+
---
|
79
80
|
### load(`Jfilepath`, `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `skipDuplicated`=False, `_isDebug_`=False)
|
80
81
|
_Deserialize a JSON format string from a file to a class `JSON_DUPLICATE_KEYS`_
|
81
82
|
- `Jfilepath`: The path to the file containing the JSON format string
|
@@ -96,8 +97,8 @@ JDKSObject = jdks.load(Jfilepath)
|
|
96
97
|
print(JDKSObject)
|
97
98
|
# OUTPUT: <json_duplicate_keys.JSON_DUPLICATE_KEYS object at 0x00000270AE986D40>
|
98
99
|
```
|
99
|
-
---
|
100
100
|
|
101
|
+
---
|
101
102
|
### JSON_DUPLICATE_KEYS.getObject()
|
102
103
|
_Get the JSON object_
|
103
104
|
```python
|
@@ -110,8 +111,8 @@ JDKSObject = jdks.loads(Jstr)
|
|
110
111
|
print(JDKSObject.getObject())
|
111
112
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan', 'version': '22.3.3', 'release': [{'version': 'latest'}]}}
|
112
113
|
```
|
113
|
-
---
|
114
114
|
|
115
|
+
---
|
115
116
|
### JSON_DUPLICATE_KEYS.get(`name`, `case_insensitive`=False, `separator`="||", `parse_index`="$", `_isDebug_`=False)
|
116
117
|
_Get value in the JSON object by `name`_
|
117
118
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -135,8 +136,8 @@ print(JDKSObject.get("release||$0$"))
|
|
135
136
|
print(JDKSObject.get("snapshot||author"))
|
136
137
|
# OUTPUT: {'name': 'snapshot||author', 'value': 'truocphan'}
|
137
138
|
```
|
138
|
-
---
|
139
139
|
|
140
|
+
---
|
140
141
|
### JSON_DUPLICATE_KEYS.set(`name`, `value`, `case_insensitive`=False, `separator`="||", `parse_index`="$", `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `_isDebug_`=False)
|
141
142
|
_Set a new `name` and `value` for the JSON object_
|
142
143
|
- `name`: new key name for the JSON object. Supported flat key name format
|
@@ -181,8 +182,8 @@ JDKSObject.set("snapshot||author", "truocphan")
|
|
181
182
|
print(JDKSObject.getObject())
|
182
183
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan'}}
|
183
184
|
```
|
184
|
-
---
|
185
185
|
|
186
|
+
---
|
186
187
|
### JSON_DUPLICATE_KEYS.insert(`name`, `value`, `position`=None, `case_insensitive`=False, `separator`="||", `parse_index`="$", `dupSign_start`="{{{", `dupSign_end`="}}}", `_isDebug_`=False)
|
187
188
|
_Insert `value` at `position` in value list of `name`_
|
188
189
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -209,9 +210,19 @@ JDKSObject.insert("snapshot||release", {'version': '2025.1.1'}, 0)
|
|
209
210
|
|
210
211
|
print(JDKSObject.getObject())
|
211
212
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}, {'version': '2025.1.1'}], 'snapshot': {'author': 'truocphan', 'version': '22.3.3', 'release': [{'version': '2025.1.1'}, {'version': 'latest'}]}}
|
213
|
+
|
214
|
+
|
215
|
+
JDKSObject = jdks.loads("[]")
|
216
|
+
|
217
|
+
JDKSObject.insert(None, {"release": []})
|
218
|
+
JDKSObject.insert(None, {"author": "truocphan"}, 0)
|
219
|
+
JDKSObject.insert("$1$||release", {"version": "2025.9.9"})
|
220
|
+
|
221
|
+
print(JDKSObject.getObject())
|
222
|
+
# OUTPUT: [{'author': 'truocphan'}, {'release': [{'version': '2025.9.9'}]}]
|
212
223
|
```
|
213
|
-
---
|
214
224
|
|
225
|
+
---
|
215
226
|
### JSON_DUPLICATE_KEYS.update(`name`, `value`, `case_insensitive`=False, `allow_new_key`=False, `separator`="||", `parse_index`="$", `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `_isDebug_`=False)
|
216
227
|
_Update new `value` for existing `name` or Set a new `name` in the JSON object_
|
217
228
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -240,8 +251,8 @@ JDKSObject.update("snapshot||version", "latest")
|
|
240
251
|
print(JDKSObject.getObject())
|
241
252
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': ['22.3.3', 'latest'], 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan', 'version': 'latest', 'release': [{'version': 'latest'}]}}
|
242
253
|
```
|
243
|
-
---
|
244
254
|
|
255
|
+
---
|
245
256
|
### JSON_DUPLICATE_KEYS.delete(`name`, `case_insensitive`=False, `separator`="||", `parse_index`="$", `_isDebug_`=False)
|
246
257
|
_Delete a key-value pair in a JSON object by key `name`_
|
247
258
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -266,8 +277,8 @@ JDKSObject.delete("snapshot")
|
|
266
277
|
print(JDKSObject.getObject())
|
267
278
|
# OUTPUT: {'author': 'truocphan', 'version{{{_2_}}}': 'latest', 'release': []}
|
268
279
|
```
|
269
|
-
---
|
270
280
|
|
281
|
+
---
|
271
282
|
### JSON_DUPLICATE_KEYS.filter_keys(`name`, `separator`="||", `parse_index`="$", `ordered_dict`=False)
|
272
283
|
_Return a `JSON_DUPLICATE_KEYS` object with keys matching a pattern_
|
273
284
|
- `name`:
|
@@ -287,8 +298,8 @@ print(JDKSObject.filter_keys("version").dumps())
|
|
287
298
|
print(JDKSObject.dumps())
|
288
299
|
# OUTPUT: {"author": "truocphan", "version": "22.3.3", "version": "latest", "release": [{"version": "latest"}], "snapshot": {"author": "truocphan", "version": "22.3.3", "release": [{"version": "latest"}]}}
|
289
300
|
```
|
290
|
-
---
|
291
301
|
|
302
|
+
---
|
292
303
|
### JSON_DUPLICATE_KEYS.filter_values(`value`, `separator`="||", `parse_index`="$", `ordered_dict`=False)
|
293
304
|
_Return a `JSON_DUPLICATE_KEYS` object with values matching a pattern_
|
294
305
|
- `value`:
|
@@ -308,8 +319,8 @@ print(JDKSObject.filter_values("latest").dumps())
|
|
308
319
|
print(JDKSObject.dumps())
|
309
320
|
# OUTPUT: {"author": "truocphan", "version": "22.3.3", "version": "latest", "release": [{"version": "latest"}], "snapshot": {"author": "truocphan", "version": "22.3.3", "release": [{"version": "latest"}]}}
|
310
321
|
```
|
311
|
-
---
|
312
322
|
|
323
|
+
---
|
313
324
|
### 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)
|
314
325
|
_Serialize a JSON object to a JSON format string_
|
315
326
|
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
@@ -333,8 +344,8 @@ JDKSObject.delete("snapshot")
|
|
333
344
|
print(JDKSObject.dumps())
|
334
345
|
# OUTPUT: {"author": "truocphan", "version": "latest", "release": []}
|
335
346
|
```
|
336
|
-
---
|
337
347
|
|
348
|
+
---
|
338
349
|
### 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)
|
339
350
|
_Serialize a JSON object to a JSON format string and write to a file_
|
340
351
|
- `Jfilepath`: the path to the file to save the JSON format string
|
@@ -363,8 +374,8 @@ JDKSObject_load = jdks.load(Jfilepath)
|
|
363
374
|
print(JDKSObject_load.getObject())
|
364
375
|
# OUTPUT: {'author': 'truocphan', 'version': 'latest', 'release': []}
|
365
376
|
```
|
366
|
-
---
|
367
377
|
|
378
|
+
---
|
368
379
|
### JSON_DUPLICATE_KEYS.flatten(`separator`="||", `parse_index`="$", `ordered_dict`=False, `_isDebug_`=False)
|
369
380
|
_Flatten a JSON object to a single key-value pairs_
|
370
381
|
- `separator`: Separator for flatten keys (default: `||`)
|
@@ -386,8 +397,8 @@ JDKSObject.flatten()
|
|
386
397
|
print(JDKSObject.getObject())
|
387
398
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release||$0$||version': 'latest', 'snapshot||author': 'truocphan', 'snapshot||version': '22.3.3', 'snapshot||release||$0$||version': 'latest'}
|
388
399
|
```
|
389
|
-
---
|
390
400
|
|
401
|
+
---
|
391
402
|
### JSON_DUPLICATE_KEYS.unflatten(`separator`="||", `parse_index`="$", `ordered_dict`=False, `_isDebug_`=False)
|
392
403
|
_Unflatten a flattened JSON object back to a JSON object_
|
393
404
|
- `separator`: Separator for flatten keys (default: `||`)
|
@@ -409,33 +420,41 @@ JDKSObject.unflatten()
|
|
409
420
|
print(JDKSObject.getObject())
|
410
421
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan', 'version': '22.3.3', 'release': [{'version': 'latest'}]}}
|
411
422
|
```
|
423
|
+
|
412
424
|
---
|
425
|
+
# 📝 CHANGELOG
|
426
|
+
### [json-duplicate-keys v2025.9.9](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.9.9)
|
427
|
+
- **Fixed**: Inserting a value with an `empty` or `null` name
|
413
428
|
|
414
|
-
|
415
|
-
|
416
|
-
-
|
417
|
-
- [**Updated**] Allow loading of JSON data in byte string format
|
418
|
-
- [**Updated**] Issue with getting and setting an empty list
|
429
|
+
### [json-duplicate-keys v2025.8.19](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.8.19)
|
430
|
+
- **Updated**: Add an exception when loading a non-existent file
|
431
|
+
- **Updated**: Dump Unicode characters to a file
|
419
432
|
|
420
|
-
|
421
|
-
-
|
433
|
+
### [json-duplicate-keys v2025.7.1](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.7.1)
|
434
|
+
- **Updated**: Fixed some issues when loading JSON strings with `skipDuplicated` option
|
435
|
+
- **Updated**: Allow loading of JSON data in byte string format
|
436
|
+
- **Updated**: Issue with getting and setting an empty list
|
422
437
|
|
423
|
-
|
438
|
+
### [json-duplicate-keys v2025.6.6](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.6.6)
|
439
|
+
- **Updated** Added `skipDuplicated` parameter to `load` and `loads` functions to improve performance when parsing large JSON strings by skipping duplicate keys.
|
440
|
+
|
441
|
+
### [json-duplicate-keys v2024.12.12](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.12.12)
|
424
442
|
- **New**: _insert_ function
|
425
443
|
|
426
|
-
|
444
|
+
### [json-duplicate-keys v2024.11.28](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.11.28)
|
427
445
|
- **Fixed**: Add subkey name to empty dict of existing key name
|
428
446
|
|
429
|
-
|
447
|
+
### [json-duplicate-keys v2024.11.19](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.11.19)
|
430
448
|
- **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
449
|
|
432
|
-
|
433
|
-
- **Fixed**: issue
|
450
|
+
### [json-duplicate-keys v2024.7.17](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.7.17)
|
451
|
+
- **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
452
|
|
435
|
-
|
453
|
+
### [json-duplicate-keys v2024.4.20](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.4.20)
|
436
454
|
- **New**: _filter_values_
|
437
455
|
- **Updated**: _filter_keys_
|
438
456
|
|
439
|
-
|
457
|
+
### [json-duplicate-keys v2024.3.24](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.3.24)
|
440
458
|
- **Updated**: _normalize_key_, _loads_, _get_, _set_, _update_, _delete_
|
459
|
+
|
441
460
|
---
|
@@ -1,47 +1,35 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Classifier: Programming Language :: Python :: 2
|
12
|
-
Classifier: Programming Language :: Python :: Implementation :: Jython
|
13
|
-
Description-Content-Type: text/markdown
|
14
|
-
License-File: LICENSE
|
15
|
-
|
16
|
-
# JSON Duplicate Keys - PyPI
|
17
|
-
Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys
|
18
|
-
|
19
|
-
<p align="center">
|
20
|
-
<a href="https://github.com/truocphan/json-duplicate-keys/releases/"><img src="https://img.shields.io/github/release/truocphan/json-duplicate-keys" height=30></a>
|
21
|
-
<a href="#"><img src="https://img.shields.io/github/downloads/truocphan/json-duplicate-keys/total" height=30></a>
|
22
|
-
<a href="#"><img src="https://img.shields.io/github/stars/truocphan/json-duplicate-keys" height=30></a>
|
23
|
-
<a href="#"><img src="https://img.shields.io/github/forks/truocphan/json-duplicate-keys" height=30></a>
|
24
|
-
<a href="https://github.com/truocphan/json-duplicate-keys/issues?q=is%3Aopen+is%3Aissue"><img src="https://img.shields.io/github/issues/truocphan/json-duplicate-keys" height=30></a>
|
25
|
-
<a href="https://github.com/truocphan/json-duplicate-keys/issues?q=is%3Aissue+is%3Aclosed"><img src="https://img.shields.io/github/issues-closed/truocphan/json-duplicate-keys" height=30></a>
|
1
|
+
<div align="center">
|
2
|
+
<h1>JSON Duplicate Keys - PyPI</h1>
|
3
|
+
<i>Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys</i>
|
4
|
+
<br><br>
|
5
|
+
<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>
|
6
|
+
<a href="#"><img src="https://img.shields.io/github/downloads/TPCyberSec/json-duplicate-keys/total" height=30></a>
|
7
|
+
<a href="#"><img src="https://img.shields.io/github/stars/TPCyberSec/json-duplicate-keys" height=30></a>
|
8
|
+
<a href="#"><img src="https://img.shields.io/github/forks/TPCyberSec/json-duplicate-keys" height=30></a>
|
9
|
+
<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>
|
10
|
+
<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
11
|
<br>
|
27
12
|
<a href="#"><img src="https://img.shields.io/pypi/v/json-duplicate-keys" height=30></a>
|
13
|
+
<a href="#"><img src="https://img.shields.io/pypi/pyversions/json-duplicate-keys" height=30></a>
|
28
14
|
<a href="#"><img src="https://img.shields.io/pypi/dm/json-duplicate-keys" height=30></a>
|
29
|
-
</
|
15
|
+
</div>
|
30
16
|
|
31
|
-
|
17
|
+
---
|
18
|
+
# 🛠️ Installation
|
32
19
|
#### From PyPI:
|
33
20
|
```console
|
34
21
|
pip install json-duplicate-keys
|
35
22
|
```
|
36
23
|
#### From Source:
|
37
24
|
```console
|
38
|
-
git clone https://github.com/
|
25
|
+
git clone https://github.com/TPCyberSec/json-duplicate-keys.git --branch <Branch/Tag>
|
39
26
|
cd json-duplicate-keys
|
40
|
-
python
|
41
|
-
python
|
27
|
+
python -m build
|
28
|
+
python -m pip install dist/json_duplicate_keys-<version>-py3-none-any.whl
|
42
29
|
```
|
43
30
|
|
44
|
-
|
31
|
+
---
|
32
|
+
# 📘 Basic Usage
|
45
33
|
### normalize_key(`name`, `dupSign_start`="{{{", `dupSign_end`="}}}", `_isDebug_`=False)
|
46
34
|
_Normalize Key name_
|
47
35
|
- `name`: key name
|
@@ -54,8 +42,8 @@ import json_duplicate_keys as jdks
|
|
54
42
|
print(jdks.normalize_key("version{{{_2_}}}"))
|
55
43
|
# OUTPUT: version
|
56
44
|
```
|
57
|
-
---
|
58
45
|
|
46
|
+
---
|
59
47
|
### loads(`Jstr`, `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `skipDuplicated`=False, `_isDebug_`=False)
|
60
48
|
_Deserialize a JSON format string to a class `JSON_DUPLICATE_KEYS`_
|
61
49
|
- `Jstr`: a JSON format string
|
@@ -74,8 +62,8 @@ JDKSObject = jdks.loads(Jstr)
|
|
74
62
|
print(JDKSObject)
|
75
63
|
# OUTPUT: <json_duplicate_keys.JSON_DUPLICATE_KEYS object at 0x00000270AE987940>
|
76
64
|
```
|
77
|
-
---
|
78
65
|
|
66
|
+
---
|
79
67
|
### load(`Jfilepath`, `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `skipDuplicated`=False, `_isDebug_`=False)
|
80
68
|
_Deserialize a JSON format string from a file to a class `JSON_DUPLICATE_KEYS`_
|
81
69
|
- `Jfilepath`: The path to the file containing the JSON format string
|
@@ -96,8 +84,8 @@ JDKSObject = jdks.load(Jfilepath)
|
|
96
84
|
print(JDKSObject)
|
97
85
|
# OUTPUT: <json_duplicate_keys.JSON_DUPLICATE_KEYS object at 0x00000270AE986D40>
|
98
86
|
```
|
99
|
-
---
|
100
87
|
|
88
|
+
---
|
101
89
|
### JSON_DUPLICATE_KEYS.getObject()
|
102
90
|
_Get the JSON object_
|
103
91
|
```python
|
@@ -110,8 +98,8 @@ JDKSObject = jdks.loads(Jstr)
|
|
110
98
|
print(JDKSObject.getObject())
|
111
99
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan', 'version': '22.3.3', 'release': [{'version': 'latest'}]}}
|
112
100
|
```
|
113
|
-
---
|
114
101
|
|
102
|
+
---
|
115
103
|
### JSON_DUPLICATE_KEYS.get(`name`, `case_insensitive`=False, `separator`="||", `parse_index`="$", `_isDebug_`=False)
|
116
104
|
_Get value in the JSON object by `name`_
|
117
105
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -135,8 +123,8 @@ print(JDKSObject.get("release||$0$"))
|
|
135
123
|
print(JDKSObject.get("snapshot||author"))
|
136
124
|
# OUTPUT: {'name': 'snapshot||author', 'value': 'truocphan'}
|
137
125
|
```
|
138
|
-
---
|
139
126
|
|
127
|
+
---
|
140
128
|
### JSON_DUPLICATE_KEYS.set(`name`, `value`, `case_insensitive`=False, `separator`="||", `parse_index`="$", `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `_isDebug_`=False)
|
141
129
|
_Set a new `name` and `value` for the JSON object_
|
142
130
|
- `name`: new key name for the JSON object. Supported flat key name format
|
@@ -181,8 +169,8 @@ JDKSObject.set("snapshot||author", "truocphan")
|
|
181
169
|
print(JDKSObject.getObject())
|
182
170
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan'}}
|
183
171
|
```
|
184
|
-
---
|
185
172
|
|
173
|
+
---
|
186
174
|
### JSON_DUPLICATE_KEYS.insert(`name`, `value`, `position`=None, `case_insensitive`=False, `separator`="||", `parse_index`="$", `dupSign_start`="{{{", `dupSign_end`="}}}", `_isDebug_`=False)
|
187
175
|
_Insert `value` at `position` in value list of `name`_
|
188
176
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -209,9 +197,19 @@ JDKSObject.insert("snapshot||release", {'version': '2025.1.1'}, 0)
|
|
209
197
|
|
210
198
|
print(JDKSObject.getObject())
|
211
199
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}, {'version': '2025.1.1'}], 'snapshot': {'author': 'truocphan', 'version': '22.3.3', 'release': [{'version': '2025.1.1'}, {'version': 'latest'}]}}
|
200
|
+
|
201
|
+
|
202
|
+
JDKSObject = jdks.loads("[]")
|
203
|
+
|
204
|
+
JDKSObject.insert(None, {"release": []})
|
205
|
+
JDKSObject.insert(None, {"author": "truocphan"}, 0)
|
206
|
+
JDKSObject.insert("$1$||release", {"version": "2025.9.9"})
|
207
|
+
|
208
|
+
print(JDKSObject.getObject())
|
209
|
+
# OUTPUT: [{'author': 'truocphan'}, {'release': [{'version': '2025.9.9'}]}]
|
212
210
|
```
|
213
|
-
---
|
214
211
|
|
212
|
+
---
|
215
213
|
### JSON_DUPLICATE_KEYS.update(`name`, `value`, `case_insensitive`=False, `allow_new_key`=False, `separator`="||", `parse_index`="$", `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `_isDebug_`=False)
|
216
214
|
_Update new `value` for existing `name` or Set a new `name` in the JSON object_
|
217
215
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -240,8 +238,8 @@ JDKSObject.update("snapshot||version", "latest")
|
|
240
238
|
print(JDKSObject.getObject())
|
241
239
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': ['22.3.3', 'latest'], 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan', 'version': 'latest', 'release': [{'version': 'latest'}]}}
|
242
240
|
```
|
243
|
-
---
|
244
241
|
|
242
|
+
---
|
245
243
|
### JSON_DUPLICATE_KEYS.delete(`name`, `case_insensitive`=False, `separator`="||", `parse_index`="$", `_isDebug_`=False)
|
246
244
|
_Delete a key-value pair in a JSON object by key `name`_
|
247
245
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -266,8 +264,8 @@ JDKSObject.delete("snapshot")
|
|
266
264
|
print(JDKSObject.getObject())
|
267
265
|
# OUTPUT: {'author': 'truocphan', 'version{{{_2_}}}': 'latest', 'release': []}
|
268
266
|
```
|
269
|
-
---
|
270
267
|
|
268
|
+
---
|
271
269
|
### JSON_DUPLICATE_KEYS.filter_keys(`name`, `separator`="||", `parse_index`="$", `ordered_dict`=False)
|
272
270
|
_Return a `JSON_DUPLICATE_KEYS` object with keys matching a pattern_
|
273
271
|
- `name`:
|
@@ -287,8 +285,8 @@ print(JDKSObject.filter_keys("version").dumps())
|
|
287
285
|
print(JDKSObject.dumps())
|
288
286
|
# OUTPUT: {"author": "truocphan", "version": "22.3.3", "version": "latest", "release": [{"version": "latest"}], "snapshot": {"author": "truocphan", "version": "22.3.3", "release": [{"version": "latest"}]}}
|
289
287
|
```
|
290
|
-
---
|
291
288
|
|
289
|
+
---
|
292
290
|
### JSON_DUPLICATE_KEYS.filter_values(`value`, `separator`="||", `parse_index`="$", `ordered_dict`=False)
|
293
291
|
_Return a `JSON_DUPLICATE_KEYS` object with values matching a pattern_
|
294
292
|
- `value`:
|
@@ -308,8 +306,8 @@ print(JDKSObject.filter_values("latest").dumps())
|
|
308
306
|
print(JDKSObject.dumps())
|
309
307
|
# OUTPUT: {"author": "truocphan", "version": "22.3.3", "version": "latest", "release": [{"version": "latest"}], "snapshot": {"author": "truocphan", "version": "22.3.3", "release": [{"version": "latest"}]}}
|
310
308
|
```
|
311
|
-
---
|
312
309
|
|
310
|
+
---
|
313
311
|
### 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)
|
314
312
|
_Serialize a JSON object to a JSON format string_
|
315
313
|
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
@@ -333,8 +331,8 @@ JDKSObject.delete("snapshot")
|
|
333
331
|
print(JDKSObject.dumps())
|
334
332
|
# OUTPUT: {"author": "truocphan", "version": "latest", "release": []}
|
335
333
|
```
|
336
|
-
---
|
337
334
|
|
335
|
+
---
|
338
336
|
### 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)
|
339
337
|
_Serialize a JSON object to a JSON format string and write to a file_
|
340
338
|
- `Jfilepath`: the path to the file to save the JSON format string
|
@@ -363,8 +361,8 @@ JDKSObject_load = jdks.load(Jfilepath)
|
|
363
361
|
print(JDKSObject_load.getObject())
|
364
362
|
# OUTPUT: {'author': 'truocphan', 'version': 'latest', 'release': []}
|
365
363
|
```
|
366
|
-
---
|
367
364
|
|
365
|
+
---
|
368
366
|
### JSON_DUPLICATE_KEYS.flatten(`separator`="||", `parse_index`="$", `ordered_dict`=False, `_isDebug_`=False)
|
369
367
|
_Flatten a JSON object to a single key-value pairs_
|
370
368
|
- `separator`: Separator for flatten keys (default: `||`)
|
@@ -386,8 +384,8 @@ JDKSObject.flatten()
|
|
386
384
|
print(JDKSObject.getObject())
|
387
385
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release||$0$||version': 'latest', 'snapshot||author': 'truocphan', 'snapshot||version': '22.3.3', 'snapshot||release||$0$||version': 'latest'}
|
388
386
|
```
|
389
|
-
---
|
390
387
|
|
388
|
+
---
|
391
389
|
### JSON_DUPLICATE_KEYS.unflatten(`separator`="||", `parse_index`="$", `ordered_dict`=False, `_isDebug_`=False)
|
392
390
|
_Unflatten a flattened JSON object back to a JSON object_
|
393
391
|
- `separator`: Separator for flatten keys (default: `||`)
|
@@ -409,33 +407,41 @@ JDKSObject.unflatten()
|
|
409
407
|
print(JDKSObject.getObject())
|
410
408
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan', 'version': '22.3.3', 'release': [{'version': 'latest'}]}}
|
411
409
|
```
|
410
|
+
|
412
411
|
---
|
412
|
+
# 📝 CHANGELOG
|
413
|
+
### [json-duplicate-keys v2025.9.9](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.9.9)
|
414
|
+
- **Fixed**: Inserting a value with an `empty` or `null` name
|
415
|
+
|
416
|
+
### [json-duplicate-keys v2025.8.19](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.8.19)
|
417
|
+
- **Updated**: Add an exception when loading a non-existent file
|
418
|
+
- **Updated**: Dump Unicode characters to a file
|
413
419
|
|
414
|
-
|
415
|
-
|
416
|
-
-
|
417
|
-
-
|
418
|
-
- [**Updated**] Issue with getting and setting an empty list
|
420
|
+
### [json-duplicate-keys v2025.7.1](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.7.1)
|
421
|
+
- **Updated**: Fixed some issues when loading JSON strings with `skipDuplicated` option
|
422
|
+
- **Updated**: Allow loading of JSON data in byte string format
|
423
|
+
- **Updated**: Issue with getting and setting an empty list
|
419
424
|
|
420
|
-
|
421
|
-
-
|
425
|
+
### [json-duplicate-keys v2025.6.6](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.6.6)
|
426
|
+
- **Updated** Added `skipDuplicated` parameter to `load` and `loads` functions to improve performance when parsing large JSON strings by skipping duplicate keys.
|
422
427
|
|
423
|
-
|
428
|
+
### [json-duplicate-keys v2024.12.12](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.12.12)
|
424
429
|
- **New**: _insert_ function
|
425
430
|
|
426
|
-
|
431
|
+
### [json-duplicate-keys v2024.11.28](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.11.28)
|
427
432
|
- **Fixed**: Add subkey name to empty dict of existing key name
|
428
433
|
|
429
|
-
|
434
|
+
### [json-duplicate-keys v2024.11.19](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.11.19)
|
430
435
|
- **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
436
|
|
432
|
-
|
433
|
-
- **Fixed**: issue
|
437
|
+
### [json-duplicate-keys v2024.7.17](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.7.17)
|
438
|
+
- **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
439
|
|
435
|
-
|
440
|
+
### [json-duplicate-keys v2024.4.20](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.4.20)
|
436
441
|
- **New**: _filter_values_
|
437
442
|
- **Updated**: _filter_keys_
|
438
443
|
|
439
|
-
|
444
|
+
### [json-duplicate-keys v2024.3.24](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.3.24)
|
440
445
|
- **Updated**: _normalize_key_, _loads_, _get_, _set_, _update_, _delete_
|
441
|
-
|
446
|
+
|
447
|
+
---
|
{json_duplicate_keys-2025.7.1 → json_duplicate_keys-2025.9.9}/json_duplicate_keys/__init__.py
RENAMED
@@ -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
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -360,8 +362,8 @@ class JSON_DUPLICATE_KEYS:
|
|
360
362
|
# User input data type validation
|
361
363
|
if type(_isDebug_) != bool: _isDebug_ = False
|
362
364
|
|
363
|
-
if type(name) not in [str, unicode]:
|
364
|
-
if _isDebug_: print("\x1b[31m[-] DataTypeError: the KEY name must be str or
|
365
|
+
if type(name) not in [str, unicode, type(None)]:
|
366
|
+
if _isDebug_: print("\x1b[31m[-] DataTypeError: the KEY name must be str, unicode or None, not {}\x1b[0m".format(type(name)))
|
365
367
|
return False
|
366
368
|
|
367
369
|
if type(position) != int: position = None
|
@@ -379,6 +381,12 @@ class JSON_DUPLICATE_KEYS:
|
|
379
381
|
if type(self.getObject()) not in [list, dict, OrderedDict]:
|
380
382
|
if _isDebug_: print("\x1b[31m[-] DataTypeError: the JSON object must be list, dict or OrderedDict, not {}\x1b[0m".format(type(self.getObject())))
|
381
383
|
return False
|
384
|
+
|
385
|
+
if (name is None or name == "") and type(self.getObject()) == list:
|
386
|
+
if position is None: position = len(self.getObject())
|
387
|
+
|
388
|
+
self.getObject().insert(position, value)
|
389
|
+
return True
|
382
390
|
|
383
391
|
if re.search(re.escape(separator)+"$", name):
|
384
392
|
if _isDebug_: print("\x1b[31m[-] KeyNameInvalidError: \x1b[0m"+name)
|
@@ -492,10 +500,10 @@ class JSON_DUPLICATE_KEYS:
|
|
492
500
|
for k, v in JDKSObject.getObject().items():
|
493
501
|
if type(k) == str and type(name) == str:
|
494
502
|
if re.search(name, k):
|
495
|
-
newJDKSObject.set(k, v, separator="§§"+separator+"§§", parse_index="§§"+parse_index+"§§", ordered_dict=ordered_dict)
|
503
|
+
newJDKSObject.set(k, v, separator=u"§§"+separator+u"§§", parse_index=u"§§"+parse_index+u"§§", ordered_dict=ordered_dict)
|
496
504
|
else:
|
497
505
|
if name == k:
|
498
|
-
newJDKSObject.set(k, v, separator="§§"+separator+"§§", parse_index="§§"+parse_index+"§§", ordered_dict=ordered_dict)
|
506
|
+
newJDKSObject.set(k, v, separator=u"§§"+separator+u"§§", parse_index=u"§§"+parse_index+u"§§", ordered_dict=ordered_dict)
|
499
507
|
|
500
508
|
return newJDKSObject
|
501
509
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -514,10 +522,10 @@ class JSON_DUPLICATE_KEYS:
|
|
514
522
|
for k, v in JDKSObject.getObject().items():
|
515
523
|
if type(v) == str and type(value) == str:
|
516
524
|
if re.search(value, v):
|
517
|
-
newJDKSObject.set(k, v, separator="§§"+separator+"§§", parse_index="§§"+parse_index+"§§", ordered_dict=ordered_dict)
|
525
|
+
newJDKSObject.set(k, v, separator=u"§§"+separator+u"§§", parse_index=u"§§"+parse_index+u"§§", ordered_dict=ordered_dict)
|
518
526
|
else:
|
519
527
|
if value == v:
|
520
|
-
newJDKSObject.set(k, v, separator="§§"+separator+"§§", parse_index="§§"+parse_index+"§§", ordered_dict=ordered_dict)
|
528
|
+
newJDKSObject.set(k, v, separator=u"§§"+separator+u"§§", parse_index=u"§§"+parse_index+u"§§", ordered_dict=ordered_dict)
|
521
529
|
|
522
530
|
return newJDKSObject
|
523
531
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -544,7 +552,7 @@ class JSON_DUPLICATE_KEYS:
|
|
544
552
|
|
545
553
|
dupSign_end_escape_regex = re.escape(json.dumps({dupSign_end:""})[2:-6])
|
546
554
|
|
547
|
-
return re.sub('
|
555
|
+
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
556
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
549
557
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
550
558
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
@@ -557,8 +565,8 @@ class JSON_DUPLICATE_KEYS:
|
|
557
565
|
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
566
|
|
559
567
|
try:
|
560
|
-
Jfile = open(Jfilepath, "
|
561
|
-
Jfile.write(Jstr)
|
568
|
+
Jfile = open(Jfilepath, "wb")
|
569
|
+
Jfile.write(Jstr.encode("utf-8"))
|
562
570
|
Jfile.close()
|
563
571
|
except Exception as e:
|
564
572
|
if _isDebug_: print("\x1b[31m[-] ExceptionError: {}\x1b[0m".format(e))
|
@@ -600,7 +608,7 @@ class JSON_DUPLICATE_KEYS:
|
|
600
608
|
else:
|
601
609
|
for k,v in Jobj.items():
|
602
610
|
_Jobj = v
|
603
|
-
_key =
|
611
|
+
_key = key+separator+k if key != None else k
|
604
612
|
|
605
613
|
__convert_Jobj_to_Jflat(_Jobj, _key)
|
606
614
|
elif type(Jobj) == list:
|
@@ -609,7 +617,7 @@ class JSON_DUPLICATE_KEYS:
|
|
609
617
|
else:
|
610
618
|
for i,v in enumerate(Jobj):
|
611
619
|
_Jobj = v
|
612
|
-
_key =
|
620
|
+
_key = key+separator+parse_index+str(i)+parse_index if key != None else parse_index+str(i)+parse_index
|
613
621
|
|
614
622
|
__convert_Jobj_to_Jflat(_Jobj, _key)
|
615
623
|
else:
|
@@ -1,32 +1,48 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: json-duplicate-keys
|
3
|
+
Version: 2025.9.9
|
4
|
+
Summary: Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys
|
5
|
+
Author-email: TP Cyber Security <tpcybersec2023@gmail.com>
|
6
|
+
License: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/TPCyberSec/json-duplicate-keys
|
8
|
+
Keywords: TPCyberSec,json,duplicate keys,json duplicate keys,flatten,unflatten
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Classifier: Programming Language :: Python :: Implementation :: Jython
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE
|
13
|
+
|
14
|
+
<div align="center">
|
15
|
+
<h1>JSON Duplicate Keys - PyPI</h1>
|
16
|
+
<i>Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys</i>
|
17
|
+
<br><br>
|
18
|
+
<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>
|
19
|
+
<a href="#"><img src="https://img.shields.io/github/downloads/TPCyberSec/json-duplicate-keys/total" height=30></a>
|
20
|
+
<a href="#"><img src="https://img.shields.io/github/stars/TPCyberSec/json-duplicate-keys" height=30></a>
|
21
|
+
<a href="#"><img src="https://img.shields.io/github/forks/TPCyberSec/json-duplicate-keys" height=30></a>
|
22
|
+
<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>
|
23
|
+
<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>
|
11
24
|
<br>
|
12
25
|
<a href="#"><img src="https://img.shields.io/pypi/v/json-duplicate-keys" height=30></a>
|
26
|
+
<a href="#"><img src="https://img.shields.io/pypi/pyversions/json-duplicate-keys" height=30></a>
|
13
27
|
<a href="#"><img src="https://img.shields.io/pypi/dm/json-duplicate-keys" height=30></a>
|
14
|
-
</
|
28
|
+
</div>
|
15
29
|
|
16
|
-
|
30
|
+
---
|
31
|
+
# 🛠️ Installation
|
17
32
|
#### From PyPI:
|
18
33
|
```console
|
19
34
|
pip install json-duplicate-keys
|
20
35
|
```
|
21
36
|
#### From Source:
|
22
37
|
```console
|
23
|
-
git clone https://github.com/
|
38
|
+
git clone https://github.com/TPCyberSec/json-duplicate-keys.git --branch <Branch/Tag>
|
24
39
|
cd json-duplicate-keys
|
25
|
-
python
|
26
|
-
python
|
40
|
+
python -m build
|
41
|
+
python -m pip install dist/json_duplicate_keys-<version>-py3-none-any.whl
|
27
42
|
```
|
28
43
|
|
29
|
-
|
44
|
+
---
|
45
|
+
# 📘 Basic Usage
|
30
46
|
### normalize_key(`name`, `dupSign_start`="{{{", `dupSign_end`="}}}", `_isDebug_`=False)
|
31
47
|
_Normalize Key name_
|
32
48
|
- `name`: key name
|
@@ -39,8 +55,8 @@ import json_duplicate_keys as jdks
|
|
39
55
|
print(jdks.normalize_key("version{{{_2_}}}"))
|
40
56
|
# OUTPUT: version
|
41
57
|
```
|
42
|
-
---
|
43
58
|
|
59
|
+
---
|
44
60
|
### loads(`Jstr`, `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `skipDuplicated`=False, `_isDebug_`=False)
|
45
61
|
_Deserialize a JSON format string to a class `JSON_DUPLICATE_KEYS`_
|
46
62
|
- `Jstr`: a JSON format string
|
@@ -59,8 +75,8 @@ JDKSObject = jdks.loads(Jstr)
|
|
59
75
|
print(JDKSObject)
|
60
76
|
# OUTPUT: <json_duplicate_keys.JSON_DUPLICATE_KEYS object at 0x00000270AE987940>
|
61
77
|
```
|
62
|
-
---
|
63
78
|
|
79
|
+
---
|
64
80
|
### load(`Jfilepath`, `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `skipDuplicated`=False, `_isDebug_`=False)
|
65
81
|
_Deserialize a JSON format string from a file to a class `JSON_DUPLICATE_KEYS`_
|
66
82
|
- `Jfilepath`: The path to the file containing the JSON format string
|
@@ -81,8 +97,8 @@ JDKSObject = jdks.load(Jfilepath)
|
|
81
97
|
print(JDKSObject)
|
82
98
|
# OUTPUT: <json_duplicate_keys.JSON_DUPLICATE_KEYS object at 0x00000270AE986D40>
|
83
99
|
```
|
84
|
-
---
|
85
100
|
|
101
|
+
---
|
86
102
|
### JSON_DUPLICATE_KEYS.getObject()
|
87
103
|
_Get the JSON object_
|
88
104
|
```python
|
@@ -95,8 +111,8 @@ JDKSObject = jdks.loads(Jstr)
|
|
95
111
|
print(JDKSObject.getObject())
|
96
112
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan', 'version': '22.3.3', 'release': [{'version': 'latest'}]}}
|
97
113
|
```
|
98
|
-
---
|
99
114
|
|
115
|
+
---
|
100
116
|
### JSON_DUPLICATE_KEYS.get(`name`, `case_insensitive`=False, `separator`="||", `parse_index`="$", `_isDebug_`=False)
|
101
117
|
_Get value in the JSON object by `name`_
|
102
118
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -120,8 +136,8 @@ print(JDKSObject.get("release||$0$"))
|
|
120
136
|
print(JDKSObject.get("snapshot||author"))
|
121
137
|
# OUTPUT: {'name': 'snapshot||author', 'value': 'truocphan'}
|
122
138
|
```
|
123
|
-
---
|
124
139
|
|
140
|
+
---
|
125
141
|
### JSON_DUPLICATE_KEYS.set(`name`, `value`, `case_insensitive`=False, `separator`="||", `parse_index`="$", `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `_isDebug_`=False)
|
126
142
|
_Set a new `name` and `value` for the JSON object_
|
127
143
|
- `name`: new key name for the JSON object. Supported flat key name format
|
@@ -166,8 +182,8 @@ JDKSObject.set("snapshot||author", "truocphan")
|
|
166
182
|
print(JDKSObject.getObject())
|
167
183
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan'}}
|
168
184
|
```
|
169
|
-
---
|
170
185
|
|
186
|
+
---
|
171
187
|
### JSON_DUPLICATE_KEYS.insert(`name`, `value`, `position`=None, `case_insensitive`=False, `separator`="||", `parse_index`="$", `dupSign_start`="{{{", `dupSign_end`="}}}", `_isDebug_`=False)
|
172
188
|
_Insert `value` at `position` in value list of `name`_
|
173
189
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -194,9 +210,19 @@ JDKSObject.insert("snapshot||release", {'version': '2025.1.1'}, 0)
|
|
194
210
|
|
195
211
|
print(JDKSObject.getObject())
|
196
212
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}, {'version': '2025.1.1'}], 'snapshot': {'author': 'truocphan', 'version': '22.3.3', 'release': [{'version': '2025.1.1'}, {'version': 'latest'}]}}
|
213
|
+
|
214
|
+
|
215
|
+
JDKSObject = jdks.loads("[]")
|
216
|
+
|
217
|
+
JDKSObject.insert(None, {"release": []})
|
218
|
+
JDKSObject.insert(None, {"author": "truocphan"}, 0)
|
219
|
+
JDKSObject.insert("$1$||release", {"version": "2025.9.9"})
|
220
|
+
|
221
|
+
print(JDKSObject.getObject())
|
222
|
+
# OUTPUT: [{'author': 'truocphan'}, {'release': [{'version': '2025.9.9'}]}]
|
197
223
|
```
|
198
|
-
---
|
199
224
|
|
225
|
+
---
|
200
226
|
### JSON_DUPLICATE_KEYS.update(`name`, `value`, `case_insensitive`=False, `allow_new_key`=False, `separator`="||", `parse_index`="$", `dupSign_start`="{{{", `dupSign_end`="}}}", `ordered_dict`=False, `_isDebug_`=False)
|
201
227
|
_Update new `value` for existing `name` or Set a new `name` in the JSON object_
|
202
228
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -225,8 +251,8 @@ JDKSObject.update("snapshot||version", "latest")
|
|
225
251
|
print(JDKSObject.getObject())
|
226
252
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': ['22.3.3', 'latest'], 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan', 'version': 'latest', 'release': [{'version': 'latest'}]}}
|
227
253
|
```
|
228
|
-
---
|
229
254
|
|
255
|
+
---
|
230
256
|
### JSON_DUPLICATE_KEYS.delete(`name`, `case_insensitive`=False, `separator`="||", `parse_index`="$", `_isDebug_`=False)
|
231
257
|
_Delete a key-value pair in a JSON object by key `name`_
|
232
258
|
- `name`: the key name of the JSON object. Supported flatten key name format
|
@@ -251,8 +277,8 @@ JDKSObject.delete("snapshot")
|
|
251
277
|
print(JDKSObject.getObject())
|
252
278
|
# OUTPUT: {'author': 'truocphan', 'version{{{_2_}}}': 'latest', 'release': []}
|
253
279
|
```
|
254
|
-
---
|
255
280
|
|
281
|
+
---
|
256
282
|
### JSON_DUPLICATE_KEYS.filter_keys(`name`, `separator`="||", `parse_index`="$", `ordered_dict`=False)
|
257
283
|
_Return a `JSON_DUPLICATE_KEYS` object with keys matching a pattern_
|
258
284
|
- `name`:
|
@@ -272,8 +298,8 @@ print(JDKSObject.filter_keys("version").dumps())
|
|
272
298
|
print(JDKSObject.dumps())
|
273
299
|
# OUTPUT: {"author": "truocphan", "version": "22.3.3", "version": "latest", "release": [{"version": "latest"}], "snapshot": {"author": "truocphan", "version": "22.3.3", "release": [{"version": "latest"}]}}
|
274
300
|
```
|
275
|
-
---
|
276
301
|
|
302
|
+
---
|
277
303
|
### JSON_DUPLICATE_KEYS.filter_values(`value`, `separator`="||", `parse_index`="$", `ordered_dict`=False)
|
278
304
|
_Return a `JSON_DUPLICATE_KEYS` object with values matching a pattern_
|
279
305
|
- `value`:
|
@@ -293,8 +319,8 @@ print(JDKSObject.filter_values("latest").dumps())
|
|
293
319
|
print(JDKSObject.dumps())
|
294
320
|
# OUTPUT: {"author": "truocphan", "version": "22.3.3", "version": "latest", "release": [{"version": "latest"}], "snapshot": {"author": "truocphan", "version": "22.3.3", "release": [{"version": "latest"}]}}
|
295
321
|
```
|
296
|
-
---
|
297
322
|
|
323
|
+
---
|
298
324
|
### 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)
|
299
325
|
_Serialize a JSON object to a JSON format string_
|
300
326
|
- `dupSign_start`: Start symbol for marking duplicates (default: `{{{`)
|
@@ -318,8 +344,8 @@ JDKSObject.delete("snapshot")
|
|
318
344
|
print(JDKSObject.dumps())
|
319
345
|
# OUTPUT: {"author": "truocphan", "version": "latest", "release": []}
|
320
346
|
```
|
321
|
-
---
|
322
347
|
|
348
|
+
---
|
323
349
|
### 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)
|
324
350
|
_Serialize a JSON object to a JSON format string and write to a file_
|
325
351
|
- `Jfilepath`: the path to the file to save the JSON format string
|
@@ -348,8 +374,8 @@ JDKSObject_load = jdks.load(Jfilepath)
|
|
348
374
|
print(JDKSObject_load.getObject())
|
349
375
|
# OUTPUT: {'author': 'truocphan', 'version': 'latest', 'release': []}
|
350
376
|
```
|
351
|
-
---
|
352
377
|
|
378
|
+
---
|
353
379
|
### JSON_DUPLICATE_KEYS.flatten(`separator`="||", `parse_index`="$", `ordered_dict`=False, `_isDebug_`=False)
|
354
380
|
_Flatten a JSON object to a single key-value pairs_
|
355
381
|
- `separator`: Separator for flatten keys (default: `||`)
|
@@ -371,8 +397,8 @@ JDKSObject.flatten()
|
|
371
397
|
print(JDKSObject.getObject())
|
372
398
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release||$0$||version': 'latest', 'snapshot||author': 'truocphan', 'snapshot||version': '22.3.3', 'snapshot||release||$0$||version': 'latest'}
|
373
399
|
```
|
374
|
-
---
|
375
400
|
|
401
|
+
---
|
376
402
|
### JSON_DUPLICATE_KEYS.unflatten(`separator`="||", `parse_index`="$", `ordered_dict`=False, `_isDebug_`=False)
|
377
403
|
_Unflatten a flattened JSON object back to a JSON object_
|
378
404
|
- `separator`: Separator for flatten keys (default: `||`)
|
@@ -394,33 +420,41 @@ JDKSObject.unflatten()
|
|
394
420
|
print(JDKSObject.getObject())
|
395
421
|
# OUTPUT: {'author': 'truocphan', 'version': '22.3.3', 'version{{{_2_}}}': 'latest', 'release': [{'version': 'latest'}], 'snapshot': {'author': 'truocphan', 'version': '22.3.3', 'release': [{'version': 'latest'}]}}
|
396
422
|
```
|
423
|
+
|
397
424
|
---
|
425
|
+
# 📝 CHANGELOG
|
426
|
+
### [json-duplicate-keys v2025.9.9](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.9.9)
|
427
|
+
- **Fixed**: Inserting a value with an `empty` or `null` name
|
428
|
+
|
429
|
+
### [json-duplicate-keys v2025.8.19](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.8.19)
|
430
|
+
- **Updated**: Add an exception when loading a non-existent file
|
431
|
+
- **Updated**: Dump Unicode characters to a file
|
398
432
|
|
399
|
-
|
400
|
-
|
401
|
-
-
|
402
|
-
-
|
403
|
-
- [**Updated**] Issue with getting and setting an empty list
|
433
|
+
### [json-duplicate-keys v2025.7.1](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.7.1)
|
434
|
+
- **Updated**: Fixed some issues when loading JSON strings with `skipDuplicated` option
|
435
|
+
- **Updated**: Allow loading of JSON data in byte string format
|
436
|
+
- **Updated**: Issue with getting and setting an empty list
|
404
437
|
|
405
|
-
|
406
|
-
-
|
438
|
+
### [json-duplicate-keys v2025.6.6](https://github.com/TPCyberSec/json-duplicate-keys/tree/2025.6.6)
|
439
|
+
- **Updated** Added `skipDuplicated` parameter to `load` and `loads` functions to improve performance when parsing large JSON strings by skipping duplicate keys.
|
407
440
|
|
408
|
-
|
441
|
+
### [json-duplicate-keys v2024.12.12](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.12.12)
|
409
442
|
- **New**: _insert_ function
|
410
443
|
|
411
|
-
|
444
|
+
### [json-duplicate-keys v2024.11.28](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.11.28)
|
412
445
|
- **Fixed**: Add subkey name to empty dict of existing key name
|
413
446
|
|
414
|
-
|
447
|
+
### [json-duplicate-keys v2024.11.19](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.11.19)
|
415
448
|
- **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
|
416
449
|
|
417
|
-
|
418
|
-
- **Fixed**: issue
|
450
|
+
### [json-duplicate-keys v2024.7.17](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.7.17)
|
451
|
+
- **Fixed**: issue #3 break the set function when the key's value is empty. Thanks [ptth222](https://github.com/ptth222) for reporting this issue.
|
419
452
|
|
420
|
-
|
453
|
+
### [json-duplicate-keys v2024.4.20](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.4.20)
|
421
454
|
- **New**: _filter_values_
|
422
455
|
- **Updated**: _filter_keys_
|
423
456
|
|
424
|
-
|
457
|
+
### [json-duplicate-keys v2024.3.24](https://github.com/TPCyberSec/json-duplicate-keys/tree/2024.3.24)
|
425
458
|
- **Updated**: _normalize_key_, _loads_, _get_, _set_, _update_, _delete_
|
426
|
-
|
459
|
+
|
460
|
+
---
|
@@ -0,0 +1,25 @@
|
|
1
|
+
[build-system]
|
2
|
+
requires = ["setuptools<70", "wheel"]
|
3
|
+
build-backend = "setuptools.build_meta"
|
4
|
+
|
5
|
+
[project]
|
6
|
+
name = "json-duplicate-keys"
|
7
|
+
version = "2025.9.9"
|
8
|
+
description = "Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys"
|
9
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
10
|
+
license = { text = "MIT" }
|
11
|
+
keywords = ["TPCyberSec", "json", "duplicate keys", "json duplicate keys", "flatten", "unflatten"]
|
12
|
+
classifiers = [
|
13
|
+
"Programming Language :: Python :: 3",
|
14
|
+
"Programming Language :: Python :: Implementation :: Jython"
|
15
|
+
]
|
16
|
+
|
17
|
+
authors = [
|
18
|
+
{ name = "TP Cyber Security", email = "tpcybersec2023@gmail.com" }
|
19
|
+
]
|
20
|
+
|
21
|
+
[project.urls]
|
22
|
+
Homepage = "https://github.com/TPCyberSec/json-duplicate-keys"
|
23
|
+
|
24
|
+
[tool.setuptools]
|
25
|
+
packages = ["json_duplicate_keys"]
|
@@ -1 +0,0 @@
|
|
1
|
-
include requirements.txt
|
File without changes
|
@@ -1,22 +0,0 @@
|
|
1
|
-
from json_duplicate_keys import json_duplicate_keys_VERSION
|
2
|
-
import setuptools
|
3
|
-
|
4
|
-
setuptools.setup(
|
5
|
-
name="json-duplicate-keys",
|
6
|
-
version=json_duplicate_keys_VERSION,
|
7
|
-
author="TP Cyber Security",
|
8
|
-
license="MIT",
|
9
|
-
author_email="tpcybersec2023@gmail.com",
|
10
|
-
description="Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys",
|
11
|
-
long_description=open("README.md").read(),
|
12
|
-
long_description_content_type="text/markdown",
|
13
|
-
install_requires=open("requirements.txt").read().split(),
|
14
|
-
url="https://github.com/truocphan/json-duplicate-keys",
|
15
|
-
classifiers=[
|
16
|
-
"Programming Language :: Python :: 3",
|
17
|
-
"Programming Language :: Python :: 2",
|
18
|
-
"Programming Language :: Python :: Implementation :: Jython"
|
19
|
-
],
|
20
|
-
keywords=["TPCyberSec", "json", "duplicate keys", "json duplicate keys", "flatten", "unflatten"],
|
21
|
-
packages=setuptools.find_packages(),
|
22
|
-
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|