diff --git a/opentelemetry-proto/src/proto.rs b/opentelemetry-proto/src/proto.rs index 8bc372b889..a5f918ecc6 100644 --- a/opentelemetry-proto/src/proto.rs +++ b/opentelemetry-proto/src/proto.rs @@ -1,3 +1,5 @@ +use serde::Deserialize; + /// provide serde support for proto traceIds and spanIds. /// Those are hex encoded strings in the jsons but they are byte arrays in the proto. /// See https://opentelemetry.io/docs/specs/otlp/#json-protobuf-encoding for more details @@ -74,76 +76,95 @@ pub(crate) mod serializers { } pub fn deserialize_from_value<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - struct ValueVisitor; + where + D: Deserializer<'de>, + { + struct ValueVisitor; - impl<'de> de::Visitor<'de> for ValueVisitor { - type Value = AnyValue; + #[derive(Deserialize)] + #[serde(untagged)] + enum StringOrInt { + Int(i64), + String(String), + } - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a JSON object for AnyValue") + impl StringOrInt { + fn get_int<'de, V>(&self) -> Result + where + V: de::MapAccess<'de>, + { + match self { + Self::Int(val) => Ok(*val), + Self::String(val) => Ok(val.parse::().map_err(de::Error::custom)?), + } + } } - fn visit_map(self, mut map: V) -> Result - where - V: de::MapAccess<'de>, - { - let mut value: Option = None; + impl<'de> de::Visitor<'de> for ValueVisitor { + type Value = AnyValue; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a JSON object for AnyValue") + } + + fn visit_map(self, mut map: V) -> Result + where + V: de::MapAccess<'de>, + { + let mut value: Option = None; - while let Some(key) = map.next_key::()? { - let key_str = key.as_str(); - match key_str { - "stringValue" => { - let s = map.next_value()?; - value = Some(any_value::Value::StringValue(s)); - }, - "boolValue" => { - let b = map.next_value()?; - value = Some(any_value::Value::BoolValue(b)); - }, - "intValue" => { - let value_str = map.next_value::()?; - let int_value = value_str.parse::() - .map_err(de::Error::custom)?; - value = Some(any_value::Value::IntValue(int_value)); - }, - "doubleValue" => { - let d = map.next_value()?; - value = Some(any_value::Value::DoubleValue(d)); - }, - "arrayValue" => { - let a = map.next_value()?; - value = Some(any_value::Value::ArrayValue(a)); - }, - "kvlistValue" => { - let kv = map.next_value()?; - value = Some(any_value::Value::KvlistValue(kv)); - }, - "bytesValue" => { - let bytes = map.next_value()?; - value = Some(any_value::Value::BytesValue(bytes)); - }, - _ => { - //skip unknown keys, and handle error later. - continue + while let Some(key) = map.next_key::()? { + let key_str = key.as_str(); + match key_str { + "stringValue" => { + let s = map.next_value()?; + value = Some(any_value::Value::StringValue(s)); + } + "boolValue" => { + let b = map.next_value()?; + value = Some(any_value::Value::BoolValue(b)); + } + "intValue" => { + let int_value = map.next_value::()?.get_int::()?; + value = Some(any_value::Value::IntValue(int_value)); + } + "doubleValue" => { + let d = map.next_value()?; + value = Some(any_value::Value::DoubleValue(d)); + } + "arrayValue" => { + let a = map.next_value()?; + value = Some(any_value::Value::ArrayValue(a)); + } + "kvlistValue" => { + let kv = map.next_value()?; + value = Some(any_value::Value::KvlistValue(kv)); + } + "bytesValue" => { + let bytes = map.next_value()?; + value = Some(any_value::Value::BytesValue(bytes)); + } + _ => { + //skip unknown keys, and handle error later. + continue; + } } } - } - if let Some(v) = value { - Ok(AnyValue { value: Some(v) }) - } else { - Err(de::Error::custom("Invalid data for AnyValue, no known keys found")) + if let Some(v) = value { + Ok(AnyValue { value: Some(v) }) + } else { + Err(de::Error::custom( + "Invalid data for AnyValue, no known keys found", + )) + } } } + + let value = deserializer.deserialize_map(ValueVisitor)?; + Ok(Some(value)) } - let value = deserializer.deserialize_map(ValueVisitor)?; - Ok(Some(value)) -} - pub fn serialize_u64_to_string(value: &u64, serializer: S) -> Result where S: Serializer, diff --git a/opentelemetry-proto/tests/json_deserialize.rs b/opentelemetry-proto/tests/json_deserialize.rs index f1fcd3d6b9..6632ffadd5 100644 --- a/opentelemetry-proto/tests/json_deserialize.rs +++ b/opentelemetry-proto/tests/json_deserialize.rs @@ -146,6 +146,36 @@ mod json_deserialize { keyvalue.value.unwrap().value.unwrap(), Value::StringValue("my.service".to_string()) ); + + let keyvalue: KeyValue = serde_json::from_str( + r#" +{ + "key": "service.name", + "value": { + "intValue": "303" + } + } + "#, + ) + .unwrap(); + + assert_eq!(keyvalue.key, "service.name".to_string()); + assert_eq!(keyvalue.value.unwrap().value.unwrap(), Value::IntValue(303)); + + let keyvalue: KeyValue = serde_json::from_str( + r#" +{ + "key": "service.name", + "value": { + "intValue": 303 + } + } + "#, + ) + .unwrap(); + + assert_eq!(keyvalue.key, "service.name".to_string()); + assert_eq!(keyvalue.value.unwrap().value.unwrap(), Value::IntValue(303)); } #[test]