Uploaded image for project: 'Apache Avro'
  1. Apache Avro
  2. AVRO-3645

Fix deserialization of enum with unit () type

    XMLWordPrintableJSON

Details

    Description

      Consider the following test:

      #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
      struct TestNullExternalEnum {
          a: NullExternalEnum,
      }    
      
      #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
      enum NullExternalEnum {
          Val1(()),
          Val2(u64),
      }
      
      #[test]
      fn test_from_value_null_enum() {
          let expected = TestNullExternalEnum {
              a: NullExternalEnum::Val1(()),
          };
      
          let test = Value::Record(vec![(
              "a".to_owned(),
              Value::Record(vec![
                  ("type".to_owned(), Value::String("Val1".to_owned())),
                  ("value".to_owned(), Value::Union(0, Box::new(Value::Null))),
              ]),
          )]);
          let final_value: TestNullExternalEnum = from_value(&test).unwrap();
          assert_eq!(
              final_value, expected,
              "Error deserializing null external enum"
          );
      }

      On version `0.15.0`, it fails with the following error message:

      called `Result::unwrap()` on an `Err` value: DeserializeValue("not a null") 

       

      If my understanding is correct, it fails because rust is expecting a `Value::Null` but it got a `Value::Union(0, Box::new(Value::Null))`.

      A fix that I found is to replace the `deserialize_unit`function in `de.rs` from

      fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
      where
          V: Visitor<'de>,
      {
          match *self.input {
              Value::Null => visitor.visit_unit(),
              _ => Err(de::Error::custom("not a null")),
          }
      } 

      to

      fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
      where
          V: Visitor<'de>,
      {
          match *self.input {
              Value::Null => visitor.visit_unit(),
              Value::Union(_i, ref x) => match **x {
                  Value::Null => visitor.visit_unit(),
                  _ => Err(de::Error::custom("not a null")),
              },
              _ => Err(de::Error::custom("not a null")),
          }
      } 

      i.e., we allow it to accept `Value::Union`, in the same way that the `deserialize_string` function accepts such values.

       

      If you ask yourself what is the point of having an enum with unit type, it is because apache-avro is not able to handle the following enum (as far as I know):

      enum NullExternalEnum {
          Val1,
          Val2(u64),
      } 

      I am sorry if there is any problem with this issue, it's the first time I open an issue here.

      Attachments

        Issue Links

          Activity

            People

              mgrigorov Martin Tzvetanov Grigorov
              LucasJavaudin Lucas Javaudin
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Time Tracking

                  Estimated:
                  Original Estimate - 2h
                  2h
                  Remaining:
                  Time Spent - 0.5h Remaining Estimate - 1.5h
                  1.5h
                  Logged:
                  Time Spent - 0.5h Remaining Estimate - 1.5h
                  0.5h