Skip to content

Commit 1ecb442

Browse files
committed
feat: allow sort_keys on to_json and to_python
1 parent 0a5bbfc commit 1ecb442

File tree

9 files changed

+552
-77
lines changed

9 files changed

+552
-77
lines changed

python/pydantic_core/_pydantic_core.pyi

+8
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ class SchemaSerializer:
306306
exclude_defaults: bool = False,
307307
exclude_none: bool = False,
308308
round_trip: bool = False,
309+
sort_keys: bool = False,
309310
warnings: bool | Literal['none', 'warn', 'error'] = True,
310311
fallback: Callable[[Any], Any] | None = None,
311312
serialize_as_any: bool = False,
@@ -326,6 +327,7 @@ class SchemaSerializer:
326327
exclude_defaults: Whether to exclude fields that are equal to their default value.
327328
exclude_none: Whether to exclude fields that have a value of `None`.
328329
round_trip: Whether to enable serialization and validation round-trip support.
330+
sort_keys: Whether to sort dictionary keys. If True, all dictionary keys will be sorted alphabetically, including nested dictionaries.
329331
warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors,
330332
"error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
331333
fallback: A function to call when an unknown value is encountered,
@@ -352,6 +354,7 @@ class SchemaSerializer:
352354
exclude_defaults: bool = False,
353355
exclude_none: bool = False,
354356
round_trip: bool = False,
357+
sort_keys: bool = False,
355358
warnings: bool | Literal['none', 'warn', 'error'] = True,
356359
fallback: Callable[[Any], Any] | None = None,
357360
serialize_as_any: bool = False,
@@ -371,6 +374,7 @@ class SchemaSerializer:
371374
exclude_defaults: Whether to exclude fields that are equal to their default value.
372375
exclude_none: Whether to exclude fields that have a value of `None`.
373376
round_trip: Whether to enable serialization and validation round-trip support.
377+
sort_keys: Whether to sort dictionary keys. If True, all dictionary keys will be sorted alphabetically, including nested dictionaries.
374378
warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors,
375379
"error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
376380
fallback: A function to call when an unknown value is encountered,
@@ -398,6 +402,7 @@ def to_json(
398402
by_alias: bool = True,
399403
exclude_none: bool = False,
400404
round_trip: bool = False,
405+
sort_keys: bool = False,
401406
timedelta_mode: Literal['iso8601', 'float'] = 'iso8601',
402407
bytes_mode: Literal['utf8', 'base64', 'hex'] = 'utf8',
403408
inf_nan_mode: Literal['null', 'constants', 'strings'] = 'constants',
@@ -419,6 +424,7 @@ def to_json(
419424
by_alias: Whether to use the alias names of fields.
420425
exclude_none: Whether to exclude fields that have a value of `None`.
421426
round_trip: Whether to enable serialization and validation round-trip support.
427+
sort_keys: Whether to sort dictionary keys. If True, all dictionary keys will be sorted alphabetically, including nested dictionaries.
422428
timedelta_mode: How to serialize `timedelta` objects, either `'iso8601'` or `'float'`.
423429
bytes_mode: How to serialize `bytes` objects, either `'utf8'`, `'base64'`, or `'hex'`.
424430
inf_nan_mode: How to serialize `Infinity`, `-Infinity` and `NaN` values, either `'null'`, `'constants'`, or `'strings'`.
@@ -477,6 +483,7 @@ def to_jsonable_python(
477483
by_alias: bool = True,
478484
exclude_none: bool = False,
479485
round_trip: bool = False,
486+
sort_keys: bool = False,
480487
timedelta_mode: Literal['iso8601', 'float'] = 'iso8601',
481488
bytes_mode: Literal['utf8', 'base64', 'hex'] = 'utf8',
482489
inf_nan_mode: Literal['null', 'constants', 'strings'] = 'constants',
@@ -498,6 +505,7 @@ def to_jsonable_python(
498505
by_alias: Whether to use the alias names of fields.
499506
exclude_none: Whether to exclude fields that have a value of `None`.
500507
round_trip: Whether to enable serialization and validation round-trip support.
508+
sort_keys: Whether to sort dictionary keys. If True, all dictionary keys will be sorted alphabetically, including nested dictionaries.
501509
timedelta_mode: How to serialize `timedelta` objects, either `'iso8601'` or `'float'`.
502510
bytes_mode: How to serialize `bytes` objects, either `'utf8'`, `'base64'`, or `'hex'`.
503511
inf_nan_mode: How to serialize `Infinity`, `-Infinity` and `NaN` values, either `'null'`, `'constants'`, or `'strings'`.

python/pydantic_core/core_schema.py

+3
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ def serialize_as_any(self) -> bool: ...
151151
@property
152152
def round_trip(self) -> bool: ...
153153

154+
@property
155+
def sort_keys(self) -> bool: ...
156+
154157
def mode_is_json(self) -> bool: ...
155158

156159
def __str__(self) -> str: ...

src/errors/validation_exception.rs

+1
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ impl ValidationError {
347347
None,
348348
false,
349349
false,
350+
false,
350351
true,
351352
None,
352353
DuckTypingSerMode::SchemaBased,

src/serializers/extra.rs

+8
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ impl SerializationState {
8686
by_alias: Option<bool>,
8787
exclude_none: bool,
8888
round_trip: bool,
89+
sort_keys: bool,
8990
serialize_unknown: bool,
9091
fallback: Option<&'py Bound<'_, PyAny>>,
9192
duck_typing_ser_mode: DuckTypingSerMode,
@@ -100,6 +101,7 @@ impl SerializationState {
100101
false,
101102
exclude_none,
102103
round_trip,
104+
sort_keys,
103105
&self.config,
104106
&self.rec_guard,
105107
serialize_unknown,
@@ -126,6 +128,7 @@ pub(crate) struct Extra<'a> {
126128
pub exclude_defaults: bool,
127129
pub exclude_none: bool,
128130
pub round_trip: bool,
131+
pub sort_keys: bool,
129132
pub config: &'a SerializationConfig,
130133
pub rec_guard: &'a SerRecursionState,
131134
// the next two are used for union logic
@@ -152,6 +155,7 @@ impl<'a> Extra<'a> {
152155
exclude_defaults: bool,
153156
exclude_none: bool,
154157
round_trip: bool,
158+
sort_keys: bool,
155159
config: &'a SerializationConfig,
156160
rec_guard: &'a SerRecursionState,
157161
serialize_unknown: bool,
@@ -168,6 +172,7 @@ impl<'a> Extra<'a> {
168172
exclude_defaults,
169173
exclude_none,
170174
round_trip,
175+
sort_keys,
171176
config,
172177
rec_guard,
173178
check: SerCheck::None,
@@ -236,6 +241,7 @@ pub(crate) struct ExtraOwned {
236241
exclude_defaults: bool,
237242
exclude_none: bool,
238243
round_trip: bool,
244+
sort_keys: bool,
239245
config: SerializationConfig,
240246
rec_guard: SerRecursionState,
241247
check: SerCheck,
@@ -257,6 +263,7 @@ impl ExtraOwned {
257263
exclude_defaults: extra.exclude_defaults,
258264
exclude_none: extra.exclude_none,
259265
round_trip: extra.round_trip,
266+
sort_keys: extra.sort_keys,
260267
config: extra.config.clone(),
261268
rec_guard: extra.rec_guard.clone(),
262269
check: extra.check,
@@ -279,6 +286,7 @@ impl ExtraOwned {
279286
exclude_defaults: self.exclude_defaults,
280287
exclude_none: self.exclude_none,
281288
round_trip: self.round_trip,
289+
sort_keys: self.sort_keys,
282290
config: &self.config,
283291
rec_guard: &self.rec_guard,
284292
check: self.check,

0 commit comments

Comments
 (0)