This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/master by this push:
     new e8200cd  AVRO-3232: add matches to deserialize_any union+string/map 
(#1368)
e8200cd is described below

commit e8200cd2d40d9cf4a702905e6d2eeb9d589ec57a
Author: Ultrabug <[email protected]>
AuthorDate: Fri Jan 7 14:51:55 2022 +0100

    AVRO-3232: add matches to deserialize_any union+string/map (#1368)
    
    the deserialize_any method is used when using from_value::<T>
    to deserialize a record which has fields that are not present
    in <T>
    
    the deserialize_any was missing some matches on certain
    Value variants which caused errors like:
    
    Error: DeserializeValue("Unsupported union")
    Error: DeserializeValue("incorrect value of type: String")
    
    this patch adds the missing matches and enhances the error
    message to help in understanding which type could be missing in
    the future
    
    the test case added showcases the problem while validating that
    union deserialization is actually working as well
    
    Signed-off-by: Ultrabug <[email protected]>
---
 lang/rust/src/de.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 79 insertions(+), 2 deletions(-)

diff --git a/lang/rust/src/de.rs b/lang/rust/src/de.rs
index cd275bc..4593a5d 100644
--- a/lang/rust/src/de.rs
+++ b/lang/rust/src/de.rs
@@ -254,13 +254,22 @@ impl<'a, 'de> de::Deserializer<'de> for &'a 
Deserializer<'de> {
                 Value::Null => visitor.visit_unit(),
                 Value::Boolean(b) => visitor.visit_bool(b),
                 Value::Int(i) => visitor.visit_i32(i),
-                Value::Long(i) => visitor.visit_i64(i),
+                Value::Long(i)
+                | Value::TimeMicros(i)
+                | Value::TimestampMillis(i)
+                | Value::TimestampMicros(i) => visitor.visit_i64(i),
                 Value::Float(f) => visitor.visit_f32(f),
                 Value::Double(d) => visitor.visit_f64(d),
-                _ => Err(de::Error::custom("Unsupported union")),
+                Value::Record(ref fields) => 
visitor.visit_map(StructDeserializer::new(fields)),
+                Value::Array(ref fields) => 
visitor.visit_seq(SeqDeserializer::new(fields)),
+                Value::String(ref s) => visitor.visit_str(s),
+                Value::Map(ref items) => 
visitor.visit_map(MapDeserializer::new(items)),
+                _ => Err(de::Error::custom(format!("unsupported union: {:?}", 
self.input))),
             },
             Value::Record(ref fields) => 
visitor.visit_map(StructDeserializer::new(fields)),
             Value::Array(ref fields) => 
visitor.visit_seq(SeqDeserializer::new(fields)),
+            Value::String(ref s) => visitor.visit_str(s),
+            Value::Map(ref items) => 
visitor.visit_map(MapDeserializer::new(items)),
             value => Err(de::Error::custom(format!(
                 "incorrect value of type: {:?}",
                 crate::schema::SchemaKind::from(value)
@@ -896,4 +905,72 @@ mod tests {
         assert_eq!(result.as_bytes(), raw_value);
         Ok(())
     }
+
+    #[test]
+    fn test_from_value_with_union() -> TestResult<()> {
+        // AVRO-3232 test for deserialize_any on missing fields on the 
destination struct:
+        // Error: DeserializeValue("Unsupported union")
+        // Error: DeserializeValue("incorrect value of type: String")
+        #[derive(Debug, Deserialize, PartialEq)]
+        struct RecordInUnion {
+            record_in_union: i32,
+        }
+
+        #[derive(Debug, Deserialize, PartialEq)]
+        struct StructWithMissingFields {
+            a_string: String,
+            a_record: Option<RecordInUnion>,
+            an_array: Option<[bool; 2]>,
+            a_union_map: Option<HashMap<String, i64>>,
+        }
+
+        let raw_map: HashMap<String, i64> = [
+            ("long_one".to_string(), 1),
+            ("long_two".to_string(), 2),
+            ("long_three".to_string(), 3),
+            ("time_micros_a".to_string(), 123),
+            ("timestamp_millis_b".to_string(), 234),
+            ("timestamp_micros_c".to_string(), 345),
+        ].iter().cloned().collect();
+
+        let value_map = raw_map.iter()
+            .map(|(k, v)| match k {
+                key  if key.starts_with("long_") => {(k.clone(), 
Value::Long(*v))}
+                key  if key.starts_with("time_micros_") => {(k.clone(), 
Value::TimeMicros(*v))}
+                key  if key.starts_with("timestamp_millis_") => {(k.clone(), 
Value::TimestampMillis(*v))}
+                key  if key.starts_with("timestamp_micros_") => {(k.clone(), 
Value::TimestampMicros(*v))}
+                _ => unreachable!(""),
+            })
+            .collect();
+
+        let record = Value::Record(vec![
+            ("a_string".to_string(), Value::String("a valid message 
field".to_string())),
+            ("a_non_existing_string".to_string(), Value::String("a 
string".to_string())),
+            ("a_union_string".to_string(), 
Value::Union(Box::new(Value::String("a union string".to_string())))),
+            ("a_union_long".to_string(), 
Value::Union(Box::new(Value::Long(412)))),
+            ("a_union_long".to_string(), 
Value::Union(Box::new(Value::Long(412)))),
+            ("a_time_micros".to_string(), 
Value::Union(Box::new(Value::TimeMicros(123)))),
+            ("a_non_existing_time_micros".to_string(), 
Value::Union(Box::new(Value::TimeMicros(-123)))),
+            ("a_timestamp_millis".to_string(), 
Value::Union(Box::new(Value::TimestampMillis(234)))),
+            ("a_non_existing_timestamp_millis".to_string(), 
Value::Union(Box::new(Value::TimestampMillis(-234)))),
+            ("a_timestamp_micros".to_string(), 
Value::Union(Box::new(Value::TimestampMicros(345)))),
+            ("a_non_existing_timestamp_micros".to_string(), 
Value::Union(Box::new(Value::TimestampMicros(-345)))),
+            ("a_record".to_string(), 
Value::Union(Box::new(Value::Record(vec!(("record_in_union".to_string(), 
Value::Int(-2))))))),
+            ("a_non_existing_record".to_string(), 
Value::Union(Box::new(Value::Record(vec!(("blah".to_string(), 
Value::Int(-22))))))),
+            ("an_array".to_string(), 
Value::Union(Box::new(Value::Array(vec!(Value::Boolean(true), 
Value::Boolean(false)))))),
+            ("a_non_existing_array".to_string(), 
Value::Union(Box::new(Value::Array(vec!(Value::Boolean(false), 
Value::Boolean(true)))))),
+            ("a_union_map".to_string(), 
Value::Union(Box::new(Value::Map(value_map)))),
+            ("a_non_existing_union_map".to_string(), 
Value::Union(Box::new(Value::Map(HashMap::new())))),
+        ]);
+
+        let deserialized: StructWithMissingFields = 
crate::from_value(&record)?;
+        let reference = StructWithMissingFields{
+            a_string: "a valid message field".to_string(),
+            a_record: Some(RecordInUnion { record_in_union: -2 }),
+            an_array: Some([true, false]),
+            a_union_map: Some(raw_map)
+        };
+        assert_eq!(deserialized, reference);
+        Ok(())
+    }
 }

Reply via email to