cloudwego / sonic-rs

A fast Rust JSON library based on SIMD.
https://crates.io/crates/sonic-rs
Apache License 2.0
487 stars 31 forks source link

`OwnedLazyValue` does not work in enum cases #84

Open bluealert opened 6 months ago

bluealert commented 6 months ago

Describe the bug When I have to deserialize several different json objects, I want to use enum to unify the deserialization. And I also want to use OwnedLazyValue, but I found that the OwnedLazyValue does not work in enum cases.

To Reproduce

use serde::Deserialize;
use serde_json::Value;
use sonic_rs::OwnedLazyValue;

#[derive(Debug, Deserialize)]
struct A {
    #[serde(deserialize_with = "deserialize_owned_lazy_value")]
    field1: OwnedLazyValue,
    field2: String,
}

#[derive(Debug, Deserialize)]
struct B {
    field_a: String,
    field_b: String,
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum JsonObject {
    A(A),
    B(B),
}

fn deserialize_owned_lazy_value<'de, D>(
    deserializer: D,
) -> Result<OwnedLazyValue, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let value = Value::deserialize(deserializer)?;
    sonic_rs::from_str(&value.to_string()).map_err(serde::de::Error::custom)
}

fn process_json(json_str: &str) {
    let json_obj: Result<JsonObject, _> = serde_json::from_str(json_str);

    match json_obj {
        Ok(JsonObject::A(a)) => {
            println!("A: {:?}, {:?}", a.field1, a.field2);
        }
        Ok(JsonObject::B(b)) => {
            println!("B: {:?}, {:?}", b.field_a, b.field_b);
        }
        Err(e) => eprintln!("Failed to parse JSON: {}", e),
    }
}

fn main() {
    let json_str1 = r#"{ "field1": "value1", "field2": "value2" }"#;
    let json_str2 = r#"{ "field_a": "valueA", "field_b": "valueB" }"#;

    process_json(json_str1);
    process_json(json_str2);
}

I must use deserialize_owned_lazy_value to deserialize field1.

Expected behavior

I want the OwnedLazyValue works as in not enum cases. I don't need to write my own deserialization function.

sonic-rs version: 0.3

Environment: Ubuntu 22.04, Rust 1.77

liuq19 commented 6 months ago

Thanks, i will investigate it

CPunisher commented 2 months ago

https://github.com/cloudwego/sonic-rs/blob/1a6c6753d0b21e6b02a0fccabdb0a4e3ebf56472/src/lazyvalue/de.rs#L43

I'm not sure. According to this implementation, we can know that sonic_rs::Value, sonic_rs::LazyValue and sonic_rs::OwnedLazyValue are different from serde::Value. They can only deserialize from structure strings like { ... } compared to https://github.com/serde-rs/json/blob/27a4ca9d7a62394fe8f0103f3d91de59f055a4c4/src/value/de.rs#L135

The following code works:

fn main() {
    let json_str1 = r#"{ "field1": { "value": "value1" }, "field2": "value2" }"#;
    let json_str2 = r#"{ "field_a": "valueA", "field_b": "valueB" }"#;

    process_json(json_str1);
    process_json(json_str2);
}
liuq19 commented 2 months ago

this is an expected behavior because we hacked the visitor when using Value or LazyValue, likes serve-json::RawValue.