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