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.
@@ -1,47 +1,48 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: json-duplicate-keys
3
- Version: 2025.7.1
3
+ Version: 2025.9.9
4
4
  Summary: Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys
5
- Home-page: https://github.com/truocphan/json-duplicate-keys
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
- # 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>
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
- </p>
28
+ </div>
30
29
 
31
- ## Installation
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/truocphan/json-duplicate-keys.git --branch <Branch/Tag>
38
+ git clone https://github.com/TPCyberSec/json-duplicate-keys.git --branch <Branch/Tag>
39
39
  cd json-duplicate-keys
40
- python setup.py build
41
- python setup.py install
40
+ python -m build
41
+ python -m pip install dist/json_duplicate_keys-<version>-py3-none-any.whl
42
42
  ```
43
43
 
44
- ## Basic Usage
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
- ## CHANGELOG
415
- #### [json-duplicate-keys v2025.7.1](https://github.com/truocphan/json-duplicate-keys/tree/2025.7.1)
416
- - [**Updated**] Fixed some issues when loading JSON strings with `skipDuplicated` option
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
- #### [json-duplicate-keys v2025.6.6](https://github.com/truocphan/json-duplicate-keys/tree/2025.6.6)
421
- - [**Updated**] Added `skipDuplicated` parameter to `load` and `loads` functions to improve performance when parsing large JSON strings by skipping duplicate keys.
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
- #### [json-duplicate-keys v2024.12.12](https://github.com/truocphan/json-duplicate-keys/tree/2024.12.12)
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
- #### [json-duplicate-keys v2024.11.28](https://github.com/truocphan/json-duplicate-keys/tree/2024.11.28)
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
- #### [json-duplicate-keys v2024.11.19](https://github.com/truocphan/json-duplicate-keys/tree/2024.11.19)
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
- #### [json-duplicate-keys v2024.7.17](https://github.com/truocphan/json-duplicate-keys/tree/2024.7.17)
433
- - **Fixed**: issue [#3](https://github.com/truocphan/json-duplicate-keys/issues/3) break the set function when the key's value is empty. Thanks [ptth222](https://github.com/ptth222) for reporting this 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
- #### [json-duplicate-keys v2024.4.20](https://github.com/truocphan/json-duplicate-keys/tree/2024.4.20)
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
- #### [json-duplicate-keys v2024.3.24](https://github.com/truocphan/json-duplicate-keys/tree/2024.3.24)
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
- Metadata-Version: 2.1
2
- Name: json-duplicate-keys
3
- Version: 2025.7.1
4
- Summary: Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys
5
- Home-page: https://github.com/truocphan/json-duplicate-keys
6
- Author: TP Cyber Security
7
- Author-email: tpcybersec2023@gmail.com
8
- License: MIT
9
- Keywords: TPCyberSec,json,duplicate keys,json duplicate keys,flatten,unflatten
10
- Classifier: Programming Language :: Python :: 3
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
- </p>
15
+ </div>
30
16
 
31
- ## Installation
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/truocphan/json-duplicate-keys.git --branch <Branch/Tag>
25
+ git clone https://github.com/TPCyberSec/json-duplicate-keys.git --branch <Branch/Tag>
39
26
  cd json-duplicate-keys
40
- python setup.py build
41
- python setup.py install
27
+ python -m build
28
+ python -m pip install dist/json_duplicate_keys-<version>-py3-none-any.whl
42
29
  ```
43
30
 
44
- ## Basic Usage
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
- ## CHANGELOG
415
- #### [json-duplicate-keys v2025.7.1](https://github.com/truocphan/json-duplicate-keys/tree/2025.7.1)
416
- - [**Updated**] Fixed some issues when loading JSON strings with `skipDuplicated` option
417
- - [**Updated**] Allow loading of JSON data in byte string format
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
- #### [json-duplicate-keys v2025.6.6](https://github.com/truocphan/json-duplicate-keys/tree/2025.6.6)
421
- - [**Updated**] Added `skipDuplicated` parameter to `load` and `loads` functions to improve performance when parsing large JSON strings by skipping duplicate keys.
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
- #### [json-duplicate-keys v2024.12.12](https://github.com/truocphan/json-duplicate-keys/tree/2024.12.12)
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
- #### [json-duplicate-keys v2024.11.28](https://github.com/truocphan/json-duplicate-keys/tree/2024.11.28)
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
- #### [json-duplicate-keys v2024.11.19](https://github.com/truocphan/json-duplicate-keys/tree/2024.11.19)
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
- #### [json-duplicate-keys v2024.7.17](https://github.com/truocphan/json-duplicate-keys/tree/2024.7.17)
433
- - **Fixed**: issue [#3](https://github.com/truocphan/json-duplicate-keys/issues/3) break the set function when the key's value is empty. Thanks [ptth222](https://github.com/ptth222) for reporting this 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
- #### [json-duplicate-keys v2024.4.20](https://github.com/truocphan/json-duplicate-keys/tree/2024.4.20)
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
- #### [json-duplicate-keys v2024.3.24](https://github.com/truocphan/json-duplicate-keys/tree/2024.3.24)
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
+ ---
@@ -1,10 +1,8 @@
1
1
  # -*- coding: utf-8 -*-
2
- json_duplicate_keys_VERSION = "2025.7.1"
3
-
4
2
  try:
5
- unicode # Python 2
3
+ unicode
6
4
  except NameError:
7
- unicode = str # Python 3
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('{dupSign_start}_\\d+_{dupSign_end}$'.format(dupSign_start=re.escape(dupSign_start), dupSign_end=re.escape(dupSign_end)), "", name)
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{dupSign_start}_dupSign_{dupSign_end}":'.format(dupSign_start=dupSign_start_escape, dupSign_end=dupSign_end_escape).encode(), Jstr)
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(), '"{dupSign_start}_dupSign_{dupSign_end}":'.format(dupSign_start=dupSign_start_escape, dupSign_end=dupSign_end_escape).encode(), Jstr)
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'{dupSign_start}_dupSign_{dupSign_end}"[\s\t\r\n]*:'.format(dupSign_start=dupSign_start_escape, dupSign_end=dupSign_end_escape).encode(), Jstr):
127
- Jstr = re.sub(r'{dupSign_start}_dupSign_{dupSign_end}"[\s\t\r\n]*:'.format(dupSign_start=dupSign_start_escape, dupSign_end=dupSign_end_escape).encode(), (dupSign_start_escape+"_"+str(i)+"_"+dupSign_end_escape+'":').encode(), Jstr, 1)
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{dupSign_start}_dupSign_{dupSign_end}":'.format(dupSign_start=dupSign_start_escape, dupSign_end=dupSign_end_escape), Jstr)
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]*:', '"{dupSign_start}_dupSign_{dupSign_end}":'.format(dupSign_start=dupSign_start_escape, dupSign_end=dupSign_end_escape), Jstr)
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'{dupSign_start}_dupSign_{dupSign_end}"[\s\t\r\n]*:'.format(dupSign_start=dupSign_start_escape, dupSign_end=dupSign_end_escape), Jstr):
144
- Jstr = re.sub(r'{dupSign_start}_dupSign_{dupSign_end}"[\s\t\r\n]*:'.format(dupSign_start=dupSign_start_escape, dupSign_end=dupSign_end_escape), dupSign_start_escape+"_"+str(i)+"_"+dupSign_end_escape+'":', Jstr, 1)
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
- with open(Jfilepath) as Jfile: Jstr = Jfile.read()
182
- except Exception as e:
183
- with open(Jfilepath, "rb") as Jfile: 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()
184
183
 
185
- return loads(Jstr, dupSign_start=dupSign_start, dupSign_end=dupSign_end, ordered_dict=ordered_dict, skipDuplicated=skipDuplicated, _isDebug_=_isDebug_)
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 unicode, not {}\x1b[0m".format(type(name)))
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('{dupSign_start}_\\d+_{dupSign_end}":'.format(dupSign_start=dupSign_start_escape_regex, dupSign_end=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))
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, "w")
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 = "{key}{separator}{k}".format(key=key,separator=separator,k=k) if key != None else "{k}".format(k=k)
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 = "{key}{separator}{parse_index}{i}{parse_index}".format(key=key, separator=separator, parse_index=parse_index, i=i) if key != None else "{parse_index}{i}{parse_index}".format(parse_index=parse_index, i=i)
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
- # JSON Duplicate Keys - PyPI
2
- Flatten/ Unflatten and Load(s)/ Dump(s) JSON File/ Object with Duplicate Keys
3
-
4
- <p align="center">
5
- <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>
6
- <a href="#"><img src="https://img.shields.io/github/downloads/truocphan/json-duplicate-keys/total" height=30></a>
7
- <a href="#"><img src="https://img.shields.io/github/stars/truocphan/json-duplicate-keys" height=30></a>
8
- <a href="#"><img src="https://img.shields.io/github/forks/truocphan/json-duplicate-keys" height=30></a>
9
- <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>
10
- <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
+ 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
- </p>
28
+ </div>
15
29
 
16
- ## Installation
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/truocphan/json-duplicate-keys.git --branch <Branch/Tag>
38
+ git clone https://github.com/TPCyberSec/json-duplicate-keys.git --branch <Branch/Tag>
24
39
  cd json-duplicate-keys
25
- python setup.py build
26
- python setup.py install
40
+ python -m build
41
+ python -m pip install dist/json_duplicate_keys-<version>-py3-none-any.whl
27
42
  ```
28
43
 
29
- ## Basic Usage
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
- ## CHANGELOG
400
- #### [json-duplicate-keys v2025.7.1](https://github.com/truocphan/json-duplicate-keys/tree/2025.7.1)
401
- - [**Updated**] Fixed some issues when loading JSON strings with `skipDuplicated` option
402
- - [**Updated**] Allow loading of JSON data in byte string format
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
- #### [json-duplicate-keys v2025.6.6](https://github.com/truocphan/json-duplicate-keys/tree/2025.6.6)
406
- - [**Updated**] Added `skipDuplicated` parameter to `load` and `loads` functions to improve performance when parsing large JSON strings by skipping duplicate keys.
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
- #### [json-duplicate-keys v2024.12.12](https://github.com/truocphan/json-duplicate-keys/tree/2024.12.12)
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
- #### [json-duplicate-keys v2024.11.28](https://github.com/truocphan/json-duplicate-keys/tree/2024.11.28)
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
- #### [json-duplicate-keys v2024.11.19](https://github.com/truocphan/json-duplicate-keys/tree/2024.11.19)
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
- #### [json-duplicate-keys v2024.7.17](https://github.com/truocphan/json-duplicate-keys/tree/2024.7.17)
418
- - **Fixed**: issue [#3](https://github.com/truocphan/json-duplicate-keys/issues/3) break the set function when the key's value is empty. Thanks [ptth222](https://github.com/ptth222) for reporting this 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
- #### [json-duplicate-keys v2024.4.20](https://github.com/truocphan/json-duplicate-keys/tree/2024.4.20)
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
- #### [json-duplicate-keys v2024.3.24](https://github.com/truocphan/json-duplicate-keys/tree/2024.3.24)
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
+ ---
@@ -1,8 +1,6 @@
1
1
  LICENSE
2
- MANIFEST.in
3
2
  README.md
4
- requirements.txt
5
- setup.py
3
+ pyproject.toml
6
4
  json_duplicate_keys/__init__.py
7
5
  json_duplicate_keys.egg-info/PKG-INFO
8
6
  json_duplicate_keys.egg-info/SOURCES.txt
@@ -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
- )