Skip to content

Commit 2c5c0f8

Browse files
committed
add support for serialize_by_alias
1 parent a780908 commit 2c5c0f8

File tree

18 files changed

+122
-75
lines changed

18 files changed

+122
-75
lines changed

python/pydantic_core/_pydantic_core.pyi

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ class SchemaSerializer:
283283
mode: str | None = None,
284284
include: _IncEx | None = None,
285285
exclude: _IncEx | None = None,
286-
by_alias: bool = True,
286+
by_alias: bool | None = None,
287287
exclude_unset: bool = False,
288288
exclude_defaults: bool = False,
289289
exclude_none: bool = False,
@@ -329,7 +329,7 @@ class SchemaSerializer:
329329
indent: int | None = None,
330330
include: _IncEx | None = None,
331331
exclude: _IncEx | None = None,
332-
by_alias: bool = True,
332+
by_alias: bool | None = None,
333333
exclude_unset: bool = False,
334334
exclude_defaults: bool = False,
335335
exclude_none: bool = False,
@@ -374,7 +374,7 @@ def to_json(
374374
indent: int | None = None,
375375
include: _IncEx | None = None,
376376
exclude: _IncEx | None = None,
377-
by_alias: bool = True,
377+
by_alias: bool | None = None,
378378
exclude_none: bool = False,
379379
round_trip: bool = False,
380380
timedelta_mode: Literal['iso8601', 'float'] = 'iso8601',
@@ -450,7 +450,7 @@ def to_jsonable_python(
450450
*,
451451
include: _IncEx | None = None,
452452
exclude: _IncEx | None = None,
453-
by_alias: bool = True,
453+
by_alias: bool | None = None,
454454
exclude_none: bool = False,
455455
round_trip: bool = False,
456456
timedelta_mode: Literal['iso8601', 'float'] = 'iso8601',

src/errors/validation_exception.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ impl ValidationError {
344344
let extra = state.extra(
345345
py,
346346
&SerMode::Json,
347-
true,
347+
None,
348348
false,
349349
false,
350350
true,

src/serializers/computed_fields.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ impl ComputedFields {
9898
exclude: next_exclude.as_ref(),
9999
extra: &field_extra,
100100
};
101-
let key = match extra.by_alias {
101+
let key = match extra.serialize_by_alias_or(computed_field.serialize_by_alias) {
102102
true => computed_field.alias.as_str(),
103103
false => computed_field.property_name.as_str(),
104104
};
@@ -116,6 +116,7 @@ struct ComputedField {
116116
serializer: CombinedSerializer,
117117
alias: String,
118118
alias_py: Py<PyString>,
119+
serialize_by_alias: bool,
119120
}
120121

121122
impl ComputedField {
@@ -133,12 +134,14 @@ impl ComputedField {
133134
let alias_py = schema
134135
.get_as(intern!(py, "alias"))?
135136
.unwrap_or_else(|| property_name.clone());
137+
let serialize_by_alias = config.get_as(intern!(py, "serialize_by_alias"))?.unwrap_or(false);
136138
Ok(Self {
137139
property_name: property_name.extract()?,
138140
property_name_py: property_name.into(),
139141
serializer,
140142
alias: alias_py.extract()?,
141143
alias_py: alias_py.into(),
144+
serialize_by_alias,
142145
})
143146
}
144147

@@ -163,7 +166,7 @@ impl ComputedField {
163166
if extra.exclude_none && value.is_none(py) {
164167
return Ok(());
165168
}
166-
let key = match extra.by_alias {
169+
let key = match extra.serialize_by_alias_or(self.serialize_by_alias) {
167170
true => self.alias_py.bind(py),
168171
false => property_name_py,
169172
};

src/serializers/extra.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl SerializationState {
8484
&'py self,
8585
py: Python<'py>,
8686
mode: &'py SerMode,
87-
by_alias: bool,
87+
by_alias: Option<bool>,
8888
exclude_none: bool,
8989
round_trip: bool,
9090
serialize_unknown: bool,
@@ -122,7 +122,7 @@ pub(crate) struct Extra<'a> {
122122
pub mode: &'a SerMode,
123123
pub ob_type_lookup: &'a ObTypeLookup,
124124
pub warnings: &'a CollectWarnings,
125-
pub by_alias: bool,
125+
pub by_alias: Option<bool>,
126126
pub exclude_unset: bool,
127127
pub exclude_defaults: bool,
128128
pub exclude_none: bool,
@@ -147,7 +147,7 @@ impl<'a> Extra<'a> {
147147
pub fn new(
148148
py: Python<'a>,
149149
mode: &'a SerMode,
150-
by_alias: bool,
150+
by_alias: Option<bool>,
151151
warnings: &'a CollectWarnings,
152152
exclude_unset: bool,
153153
exclude_defaults: bool,
@@ -204,6 +204,10 @@ impl<'a> Extra<'a> {
204204
pub(crate) fn model_type_name(&self) -> Option<Bound<'a, PyString>> {
205205
self.model.and_then(|model| model.get_type().name().ok())
206206
}
207+
208+
pub fn serialize_by_alias_or(&self, serialize_by_alias: bool) -> bool {
209+
self.by_alias.unwrap_or(serialize_by_alias)
210+
}
207211
}
208212

209213
#[derive(Clone, Copy, PartialEq, Eq)]
@@ -228,7 +232,7 @@ impl SerCheck {
228232
pub(crate) struct ExtraOwned {
229233
mode: SerMode,
230234
warnings: CollectWarnings,
231-
by_alias: bool,
235+
by_alias: Option<bool>,
232236
exclude_unset: bool,
233237
exclude_defaults: bool,
234238
exclude_none: bool,

src/serializers/fields.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub(super) struct SerField {
2929
// None serializer means exclude
3030
pub serializer: Option<CombinedSerializer>,
3131
pub required: bool,
32+
pub serialize_by_alias: bool,
3233
}
3334

3435
impl_py_gc_traverse!(SerField { serializer });
@@ -40,6 +41,7 @@ impl SerField {
4041
alias: Option<String>,
4142
serializer: Option<CombinedSerializer>,
4243
required: bool,
44+
serialize_by_alias: bool,
4345
) -> Self {
4446
let alias_py = alias.as_ref().map(|alias| PyString::new(py, alias.as_str()).into());
4547
Self {
@@ -48,11 +50,12 @@ impl SerField {
4850
alias_py,
4951
serializer,
5052
required,
53+
serialize_by_alias,
5154
}
5255
}
5356

5457
pub fn get_key_py<'py>(&self, py: Python<'py>, extra: &Extra) -> &Bound<'py, PyAny> {
55-
if extra.by_alias {
58+
if extra.serialize_by_alias_or(self.serialize_by_alias) {
5659
if let Some(ref alias_py) = self.alias_py {
5760
return alias_py.bind(py);
5861
}
@@ -61,7 +64,7 @@ impl SerField {
6164
}
6265

6366
pub fn get_key_json<'a>(&'a self, key_str: &'a str, extra: &Extra) -> Cow<'a, str> {
64-
if extra.by_alias {
67+
if extra.serialize_by_alias_or(self.serialize_by_alias) {
6568
if let Some(ref alias) = self.alias {
6669
return Cow::Borrowed(alias.as_str());
6770
}

src/serializers/mod.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ impl SchemaSerializer {
5454
&'b self,
5555
py: Python<'a>,
5656
mode: &'a SerMode,
57-
by_alias: bool,
57+
by_alias: Option<bool>,
5858
warnings: &'a CollectWarnings,
5959
exclude_unset: bool,
6060
exclude_defaults: bool,
@@ -106,7 +106,7 @@ impl SchemaSerializer {
106106
}
107107

108108
#[allow(clippy::too_many_arguments)]
109-
#[pyo3(signature = (value, *, mode = None, include = None, exclude = None, by_alias = true,
109+
#[pyo3(signature = (value, *, mode = None, include = None, exclude = None, by_alias = None,
110110
exclude_unset = false, exclude_defaults = false, exclude_none = false, round_trip = false, warnings = WarningsArg::Bool(true),
111111
fallback = None, serialize_as_any = false, context = None))]
112112
pub fn to_python(
@@ -116,7 +116,7 @@ impl SchemaSerializer {
116116
mode: Option<&str>,
117117
include: Option<&Bound<'_, PyAny>>,
118118
exclude: Option<&Bound<'_, PyAny>>,
119-
by_alias: bool,
119+
by_alias: Option<bool>,
120120
exclude_unset: bool,
121121
exclude_defaults: bool,
122122
exclude_none: bool,
@@ -155,7 +155,7 @@ impl SchemaSerializer {
155155
}
156156

157157
#[allow(clippy::too_many_arguments)]
158-
#[pyo3(signature = (value, *, indent = None, include = None, exclude = None, by_alias = true,
158+
#[pyo3(signature = (value, *, indent = None, include = None, exclude = None, by_alias = None,
159159
exclude_unset = false, exclude_defaults = false, exclude_none = false, round_trip = false, warnings = WarningsArg::Bool(true),
160160
fallback = None, serialize_as_any = false, context = None))]
161161
pub fn to_json(
@@ -165,7 +165,7 @@ impl SchemaSerializer {
165165
indent: Option<usize>,
166166
include: Option<&Bound<'_, PyAny>>,
167167
exclude: Option<&Bound<'_, PyAny>>,
168-
by_alias: bool,
168+
by_alias: Option<bool>,
169169
exclude_unset: bool,
170170
exclude_defaults: bool,
171171
exclude_none: bool,
@@ -239,7 +239,7 @@ impl SchemaSerializer {
239239

240240
#[allow(clippy::too_many_arguments)]
241241
#[pyfunction]
242-
#[pyo3(signature = (value, *, indent = None, include = None, exclude = None, by_alias = true,
242+
#[pyo3(signature = (value, *, indent = None, include = None, exclude = None, by_alias = None,
243243
exclude_none = false, round_trip = false, timedelta_mode = "iso8601", bytes_mode = "utf8",
244244
inf_nan_mode = "constants", serialize_unknown = false, fallback = None, serialize_as_any = false,
245245
context = None))]
@@ -249,7 +249,7 @@ pub fn to_json(
249249
indent: Option<usize>,
250250
include: Option<&Bound<'_, PyAny>>,
251251
exclude: Option<&Bound<'_, PyAny>>,
252-
by_alias: bool,
252+
by_alias: Option<bool>,
253253
exclude_none: bool,
254254
round_trip: bool,
255255
timedelta_mode: &str,
@@ -282,15 +282,15 @@ pub fn to_json(
282282

283283
#[allow(clippy::too_many_arguments)]
284284
#[pyfunction]
285-
#[pyo3(signature = (value, *, include = None, exclude = None, by_alias = true, exclude_none = false, round_trip = false,
285+
#[pyo3(signature = (value, *, include = None, exclude = None, by_alias = None, exclude_none = false, round_trip = false,
286286
timedelta_mode = "iso8601", bytes_mode = "utf8", inf_nan_mode = "constants", serialize_unknown = false, fallback = None,
287287
serialize_as_any = false, context = None))]
288288
pub fn to_jsonable_python(
289289
py: Python,
290290
value: &Bound<'_, PyAny>,
291291
include: Option<&Bound<'_, PyAny>>,
292292
exclude: Option<&Bound<'_, PyAny>>,
293-
by_alias: bool,
293+
by_alias: Option<bool>,
294294
exclude_none: bool,
295295
round_trip: bool,
296296
timedelta_mode: &str,

src/serializers/type_serializers/dataclass.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ impl BuildSerializer for DataclassArgsBuilder {
3737
_ => FieldsMode::SimpleDict,
3838
};
3939

40+
let serialize_by_alias = config.get_as(intern!(py, "serialize_by_alias"))?.unwrap_or(false);
41+
4042
for (index, item) in fields_list.iter().enumerate() {
4143
let field_info = item.downcast::<PyDict>()?;
4244
let name: String = field_info.get_as_req(intern!(py, "name"))?;
@@ -45,14 +47,17 @@ impl BuildSerializer for DataclassArgsBuilder {
4547

4648
if !field_info.get_as(intern!(py, "init_only"))?.unwrap_or(false) {
4749
if field_info.get_as(intern!(py, "serialization_exclude"))? == Some(true) {
48-
fields.insert(name, SerField::new(py, key_py, None, None, true));
50+
fields.insert(name, SerField::new(py, key_py, None, None, true, serialize_by_alias));
4951
} else {
5052
let schema = field_info.get_as_req(intern!(py, "schema"))?;
5153
let serializer = CombinedSerializer::build(&schema, config, definitions)
5254
.map_err(|e| py_schema_error_type!("Field `{}`:\n {}", index, e))?;
5355

5456
let alias = field_info.get_as(intern!(py, "serialization_alias"))?;
55-
fields.insert(name, SerField::new(py, key_py, alias, Some(serializer), true));
57+
fields.insert(
58+
name,
59+
SerField::new(py, key_py, alias, Some(serializer), true, serialize_by_alias),
60+
);
5661
}
5762
}
5863
}

src/serializers/type_serializers/function.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ struct SerializationInfo {
547547
#[pyo3(get, name = "mode")]
548548
_mode: SerMode,
549549
#[pyo3(get)]
550-
by_alias: bool,
550+
by_alias: Option<bool>,
551551
#[pyo3(get)]
552552
exclude_unset: bool,
553553
#[pyo3(get)]
@@ -668,7 +668,7 @@ impl SerializationInfo {
668668
None => "None".to_owned(),
669669
},
670670
self._mode,
671-
py_bool(self.by_alias),
671+
py_bool(self.by_alias.unwrap_or(false)),
672672
py_bool(self.exclude_unset),
673673
py_bool(self.exclude_defaults),
674674
py_bool(self.exclude_none),

src/serializers/type_serializers/model.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ impl BuildSerializer for ModelFieldsBuilder {
4747
(_, _) => None,
4848
};
4949

50+
let serialize_by_alias = config.get_as(intern!(py, "serialize_by_alias"))?.unwrap_or(false);
51+
5052
for (key, value) in fields_dict {
5153
let key_py = key.downcast_into::<PyString>()?;
5254
let key: String = key_py.extract()?;
@@ -55,15 +57,18 @@ impl BuildSerializer for ModelFieldsBuilder {
5557
let key_py: Py<PyString> = key_py.into();
5658

5759
if field_info.get_as(intern!(py, "serialization_exclude"))? == Some(true) {
58-
fields.insert(key, SerField::new(py, key_py, None, None, true));
60+
fields.insert(key, SerField::new(py, key_py, None, None, true, serialize_by_alias));
5961
} else {
6062
let alias: Option<String> = field_info.get_as(intern!(py, "serialization_alias"))?;
6163

6264
let schema = field_info.get_as_req(intern!(py, "schema"))?;
6365
let serializer = CombinedSerializer::build(&schema, config, definitions)
6466
.map_err(|e| py_schema_error_type!("Field `{}`:\n {}", key, e))?;
6567

66-
fields.insert(key, SerField::new(py, key_py, alias, Some(serializer), true));
68+
fields.insert(
69+
key,
70+
SerField::new(py, key_py, alias, Some(serializer), true, serialize_by_alias),
71+
);
6772
}
6873
}
6974

src/serializers/type_serializers/typed_dict.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ impl BuildSerializer for TypedDictBuilder {
3232
_ => FieldsMode::SimpleDict,
3333
};
3434

35+
let serialize_by_alias = config.get_as(intern!(py, "serialize_by_alias"))?.unwrap_or(false);
36+
3537
let fields_dict: Bound<'_, PyDict> = schema.get_as_req(intern!(py, "fields"))?;
3638
let mut fields: AHashMap<String, SerField> = AHashMap::with_capacity(fields_dict.len());
3739

@@ -52,14 +54,17 @@ impl BuildSerializer for TypedDictBuilder {
5254
let required = field_info.get_as(intern!(py, "required"))?.unwrap_or(total);
5355

5456
if field_info.get_as(intern!(py, "serialization_exclude"))? == Some(true) {
55-
fields.insert(key, SerField::new(py, key_py, None, None, required));
57+
fields.insert(key, SerField::new(py, key_py, None, None, required, serialize_by_alias));
5658
} else {
5759
let alias: Option<String> = field_info.get_as(intern!(py, "serialization_alias"))?;
5860

5961
let schema = field_info.get_as_req(intern!(py, "schema"))?;
6062
let serializer = CombinedSerializer::build(&schema, config, definitions)
6163
.map_err(|e| py_schema_error_type!("Field `{}`:\n {}", key, e))?;
62-
fields.insert(key, SerField::new(py, key_py, alias, Some(serializer), required));
64+
fields.insert(
65+
key,
66+
SerField::new(py, key_py, alias, Some(serializer), required, serialize_by_alias),
67+
);
6368
}
6469
}
6570

0 commit comments

Comments
 (0)