This is an automated email from the ASF dual-hosted git repository.
kriskras99 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro-rs.git
The following commit(s) were added to refs/heads/main by this push:
new f76e2ff fix: Choose correct schema when serializing an Option<Enum>
(#337)
f76e2ff is described below
commit f76e2ff90e4f8730268db1c86f9d37ffdb9a59ed
Author: Allen Ho <[email protected]>
AuthorDate: Sat Nov 22 07:37:10 2025 -0800
fix: Choose correct schema when serializing an Option<Enum> (#337)
* fix: Choose correct schema when serializing an Option<Enum>
* chore: Rename test to match Github PR number
---------
Co-authored-by: allenyuchen <[email protected]>
---
avro/src/ser_schema.rs | 194 +++++++++++++++++++++++++++++++++++++++++--------
avro/src/writer.rs | 2 +-
2 files changed, 163 insertions(+), 33 deletions(-)
diff --git a/avro/src/ser_schema.rs b/avro/src/ser_schema.rs
index 7d0ed3e..5a91eb6 100644
--- a/avro/src/ser_schema.rs
+++ b/avro/src/ser_schema.rs
@@ -1072,38 +1072,13 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
where
T: ?Sized + ser::Serialize,
{
- let create_error = |cause: String| {
- Error::new(Details::SerializeValueWithSchema {
- value_type: "some",
- value: format!("Some(?). Cause: {cause}"),
- schema: schema.clone(),
- })
- };
-
- match schema {
- Schema::Union(union_schema) => {
- for (i, variant_schema) in
union_schema.schemas.iter().enumerate() {
- match variant_schema {
- Schema::Null => { /* skip */ }
- _ => {
- encode_long(i as i64, &mut *self.writer)?;
- let mut variant_ser =
SchemaAwareWriteSerializer::new(
- &mut *self.writer,
- variant_schema,
- self.names,
- self.enclosing_namespace.clone(),
- );
- return value.serialize(&mut variant_ser);
- }
- }
- }
- Err(create_error(format!(
- "Cannot find a matching Null schema in {:?}",
- union_schema.schemas
- )))
- }
- _ => value.serialize(self),
- }
+ let mut inner_ser = SchemaAwareWriteSerializer::new(
+ &mut *self.writer,
+ schema,
+ self.names,
+ self.enclosing_namespace.clone(),
+ );
+ value.serialize(&mut inner_ser)
}
fn serialize_unit_struct_with_schema(
@@ -2770,4 +2745,159 @@ mod tests {
Ok(())
}
+
+ #[test]
+ fn avro_rs_337_serialize_union_record_variant() -> TestResult {
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "record",
+ "name": "TestRecord",
+ "fields": [{
+ "name": "innerUnion", "type": [
+ {"type": "record", "name": "innerRecordFoo", "fields": [
+ {"name": "foo", "type": "string"}
+ ]},
+ {"type": "record", "name": "innerRecordBar", "fields": [
+ {"name": "bar", "type": "string"}
+ ]},
+ {"name": "intField", "type": "int"},
+ {"name": "stringField", "type": "string"}
+ ]
+ }]
+ }"#,
+ )?;
+
+ #[derive(Serialize)]
+ #[serde(rename_all = "camelCase")]
+ struct TestRecord {
+ inner_union: InnerUnion,
+ }
+
+ #[derive(Serialize)]
+ #[serde(untagged)]
+ enum InnerUnion {
+ InnerVariantFoo(InnerRecordFoo),
+ InnerVariantBar(InnerRecordBar),
+ IntField(i32),
+ StringField(String),
+ }
+
+ #[derive(Serialize)]
+ #[serde(rename = "innerRecordFoo")]
+ struct InnerRecordFoo {
+ foo: String,
+ }
+
+ #[derive(Serialize)]
+ #[serde(rename = "innerRecordBar")]
+ struct InnerRecordBar {
+ bar: String,
+ }
+
+ let mut buffer: Vec<u8> = Vec::new();
+ let rs = ResolvedSchema::try_from(&schema)?;
+ let mut serializer =
+ SchemaAwareWriteSerializer::new(&mut buffer, &schema,
rs.get_names(), None);
+
+ let foo_record = TestRecord {
+ inner_union: InnerUnion::InnerVariantFoo(InnerRecordFoo {
+ foo: String::from("foo"),
+ }),
+ };
+ foo_record.serialize(&mut serializer)?;
+ let bar_record = TestRecord {
+ inner_union: InnerUnion::InnerVariantBar(InnerRecordBar {
+ bar: String::from("bar"),
+ }),
+ };
+ bar_record.serialize(&mut serializer)?;
+ let int_record = TestRecord {
+ inner_union: InnerUnion::IntField(1),
+ };
+ int_record.serialize(&mut serializer)?;
+ let string_record = TestRecord {
+ inner_union: InnerUnion::StringField(String::from("string")),
+ };
+ string_record.serialize(&mut serializer)?;
+ Ok(())
+ }
+
+ #[test]
+ fn avro_rs_337_serialize_option_union_record_variant() -> TestResult {
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "record",
+ "name": "TestRecord",
+ "fields": [{
+ "name": "innerUnion", "type": [
+ "null",
+ {"type": "record", "name": "innerRecordFoo", "fields": [
+ {"name": "foo", "type": "string"}
+ ]},
+ {"type": "record", "name": "innerRecordBar", "fields": [
+ {"name": "bar", "type": "string"}
+ ]},
+ {"name": "intField", "type": "int"},
+ {"name": "stringField", "type": "string"}
+ ]
+ }]
+ }"#,
+ )?;
+
+ #[derive(Serialize)]
+ #[serde(rename_all = "camelCase")]
+ struct TestRecord {
+ inner_union: Option<InnerUnion>,
+ }
+
+ #[derive(Serialize)]
+ #[serde(untagged)]
+ enum InnerUnion {
+ InnerVariantFoo(InnerRecordFoo),
+ InnerVariantBar(InnerRecordBar),
+ IntField(i32),
+ StringField(String),
+ }
+
+ #[derive(Serialize)]
+ #[serde(rename = "innerRecordFoo")]
+ struct InnerRecordFoo {
+ foo: String,
+ }
+
+ #[derive(Serialize)]
+ #[serde(rename = "innerRecordBar")]
+ struct InnerRecordBar {
+ bar: String,
+ }
+
+ let mut buffer: Vec<u8> = Vec::new();
+ let rs = ResolvedSchema::try_from(&schema)?;
+ let mut serializer =
+ SchemaAwareWriteSerializer::new(&mut buffer, &schema,
rs.get_names(), None);
+
+ let null_record = TestRecord { inner_union: None };
+ null_record.serialize(&mut serializer)?;
+ let foo_record = TestRecord {
+ inner_union: Some(InnerUnion::InnerVariantFoo(InnerRecordFoo {
+ foo: String::from("foo"),
+ })),
+ };
+ foo_record.serialize(&mut serializer)?;
+ let bar_record = TestRecord {
+ inner_union: Some(InnerUnion::InnerVariantBar(InnerRecordBar {
+ bar: String::from("bar"),
+ })),
+ };
+ bar_record.serialize(&mut serializer)?;
+ let int_record = TestRecord {
+ inner_union: Some(InnerUnion::IntField(1)),
+ };
+ int_record.serialize(&mut serializer)?;
+ let string_record = TestRecord {
+ inner_union: Some(InnerUnion::StringField(String::from("string"))),
+ };
+ string_record.serialize(&mut serializer)?;
+ Ok(())
+ }
}
diff --git a/avro/src/writer.rs b/avro/src/writer.rs
index 4bf2cde..31fb5c7 100644
--- a/avro/src/writer.rs
+++ b/avro/src/writer.rs
@@ -1653,7 +1653,7 @@ mod tests {
Err(e) => {
assert_eq!(
e.to_string(),
- r#"Failed to serialize field 'time' for record
Record(RecordSchema { name: Name { name: "Conference", namespace: None },
aliases: None, doc: None, fields: [RecordField { name: "name", doc: None,
aliases: None, default: None, schema: String, order: Ascending, position: 0,
custom_attributes: {} }, RecordField { name: "date", doc: None, aliases:
Some(["time2", "time"]), default: None, schema: Union(UnionSchema { schemas:
[Null, Long], variant_index: {Null: 0, Long: 1} }) [...]
+ r#"Failed to serialize field 'time' for record
Record(RecordSchema { name: Name { name: "Conference", namespace: None },
aliases: None, doc: None, fields: [RecordField { name: "name", doc: None,
aliases: None, default: None, schema: String, order: Ascending, position: 0,
custom_attributes: {} }, RecordField { name: "date", doc: None, aliases:
Some(["time2", "time"]), default: None, schema: Union(UnionSchema { schemas:
[Null, Long], variant_index: {Null: 0, Long: 1} }) [...]
);
}
}