Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parsing JSON with multiple representation in same attribute #473

Closed
tangc-JNPR opened this issue Aug 17, 2018 · 3 comments
Closed

Parsing JSON with multiple representation in same attribute #473

tangc-JNPR opened this issue Aug 17, 2018 · 3 comments
Labels

Comments

@tangc-JNPR
Copy link

I'm relatively new to Rust, and even more so to serde, so I'm having trouble finding out if this is even doable.
I have a JSON file which has two different representations in this form:

"coordinates": [ [ [ 121.423364, 24.9913596 ], [ 121.4233327, 24.9912977 ], ] ]

and this:
"coordinates": [ [ 121.4472492, 25.0052053 ], [ 121.4466457, 25.0028547 ] ]

It shows that it has two dimensions array and three dimensions arrays representing ways in the same attribute.
This makes the file is hard to serialize.

Below is the code that I implement

#[derive(Serialize, Deserialize, Debug)]
struct Geometry {
    #[serde(deserialize_with = "string_or_number", rename = "type")]
    geometry_type: Value,
    #[serde(default, skip_serializing_if = "Vec::is_empty", rename = "coordinates")]
    geometry_coor: Vec<Coordinates>,
    #[serde(default, skip_serializing_if = "Vec::is_empty", rename = "coordinates")]
    geometry_coor2: Vec<Vec<Coordinates>>
}

#[derive(Serialize, Deserialize, Debug)]
struct Coordinates {
    #[serde(deserialize_with = "string_or_number")]
    longitude: Value, 
    #[serde(deserialize_with = "string_or_number")]
    latitude: Value,
}

fn string_or_number<'de, D>(de: D) -> Result<Value, D::Error>
    where D: serde::Deserializer<'de>
{
    let helper: Value = Deserialize::deserialize(de)?;

    match helper {
        Value::Number(n) => {
            println!("{:#?}", n.as_f64().unwrap().to_string());
            Ok(Value::Number(n))
        },
        Value::String(s) => Ok(json!(s)),
        _ => Ok(json!(null))
    }
} 

And I have trouble in struct Geometry which serialize the file of coordinates.
So does there have any ways that I can deal with this kind of form?

@dtolnay
Copy link
Member

dtolnay commented Aug 17, 2018

I would recommend using an untagged enum to represent a coordinate array that can be 2d or 3d.
Playground link

@tangc-JNPR
Copy link
Author

Thanks for the help.
I have solved the problem.

Here is the code after modified.

#[derive(Serialize, Deserialize, Debug)]
struct Geometry {
    #[serde(deserialize_with = "string_or_number", rename = "type")]
    geometry_type: Value,
    #[serde(default, skip_serializing_if = "Vec::is_empty", rename = "coordinates")]
    geometry_coor: Vec<Coordinates_form>,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
enum Coordinates_form {
    #[serde(skip_serializing)]
    OneD(Coordinates),
    #[serde(skip_serializing)]
    TwoD(Vec<Coordinates>),
    #[serde(skip_serializing)]
    ThreeD(Vec<Vec<Coordinates>>),
}

#[derive(Deserialize, Debug)]
struct Coordinates {
    #[serde(deserialize_with = "string_or_number")]
    longitude: Value, 
    #[serde(deserialize_with = "string_or_number")]
    latitude: Value,
}

fn string_or_number<'de, D>(de: D) -> Result<Value, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let helper: Value = Deserialize::deserialize(de)?;

    match helper {
        Value::Number(n) => {
            println!("{:#?}", n.as_f64().unwrap().to_string());
            Ok(Value::Number(n))
        }
        Value::String(s) => Ok(json!(s)),
        _ => Ok(json!(null)),
    }
}

@dtolnay
Copy link
Member

dtolnay commented Aug 18, 2018

Nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

2 participants