Skip to content

Commit

Permalink
When deserializing JSON, accept both int and string in 'intValue' fie…
Browse files Browse the repository at this point in the history
…ld of

AnyValue
  • Loading branch information
Razvan Rotari committed Jul 2, 2024
1 parent 0303a38 commit 0da1f11
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 58 deletions.
137 changes: 79 additions & 58 deletions opentelemetry-proto/src/proto.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -74,76 +76,95 @@ pub(crate) mod serializers {
}

pub fn deserialize_from_value<'de, D>(deserializer: D) -> Result<Option<AnyValue>, 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<i64, V::Error>
where
V: de::MapAccess<'de>,
{
match self {
Self::Int(val) => Ok(*val),
Self::String(val) => Ok(val.parse::<i64>().map_err(de::Error::custom)?),
}
}
}

fn visit_map<V>(self, mut map: V) -> Result<AnyValue, V::Error>
where
V: de::MapAccess<'de>,
{
let mut value: Option<any_value::Value> = 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<V>(self, mut map: V) -> Result<AnyValue, V::Error>
where
V: de::MapAccess<'de>,
{
let mut value: Option<any_value::Value> = None;

while let Some(key) = map.next_key::<String>()? {
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::<String>()?;
let int_value = value_str.parse::<i64>()
.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::<String>()? {
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::<StringOrInt>()?.get_int::<V>()?;
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<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
Expand Down
30 changes: 30 additions & 0 deletions opentelemetry-proto/tests/json_deserialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down

0 comments on commit 0da1f11

Please sign in to comment.