GREsau / okapi

OpenAPI (AKA Swagger) document generation for Rust projects
MIT License
634 stars 112 forks source link

deserializing and serializing results in faulty json #67

Open andrieshiemstra opened 2 years ago

andrieshiemstra commented 2 years ago

When deserializing from json the schemas from a param also get added to the extensions object of a param, resulting in faulty json being outputted when serializing again (schema attribute is added twice)

use okapi::openapi3::{Operation, RefOr, Parameter};

fn main() {

    let json = "{\"responses\": {}, \"name\": \"someOp\", \"parameters\": [{\"in\": \"path\", \"schema\": {\"$ref\": \"#/defid\"}, \"name\": \"param1\"}]}";

    let operation: Operation = serde_json::from_str(json).unwrap();

    for param in &operation.parameters {
        match param {
            RefOr::Ref(_) => {
            }
            RefOr::Object(p) => {
                println!("p.json: {}", serde_json::to_string(p).unwrap());
                println!("p.value.json: {}", serde_json::to_string(&p.value).unwrap());
                println!("p.extensions.json: {}", serde_json::to_string(&p.extensions).unwrap());
            }
        }
    }

    println!("json: {}", serde_json::to_string(&operation).unwrap());

}

results in

p.json: {"name":"param1","in":"path","schema":{"$ref":"#/defid"},"schema":{"$ref":"#/defid"}}
p.value.json: {"schema":{"$ref":"#/defid"}}
p.extensions.json: {"schema":{"$ref":"#/defid"}}
json: {"parameters":[{"name":"param1","in":"path","schema":{"$ref":"#/defid"},"schema":{"$ref":"#/defid"}}],"responses":{},"name":"someOp"}

The same happens when deserializing an operation with responses, deserializing and serializing this

   "responses": {
      "200": {
        "description": "info to be returned",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/getInfoResultSchema"
            }
          }
        }
      },
      "default": {
        "description": "Unexpected error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/DefaultError"
            }
          }
        }
      }
    }

results in this

"responses": {
          "default": {
            "description": "Unexpected error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DefaultError"
                }
              }
            }
          },
          "200": {
            "description": "info to be returned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/getInfoResultSchema"
                }
              }
            }
          },
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/getInfoResultSchema"
                }
              }
            },
            "description": "info to be returned"
          }
        }
ralpha commented 2 years ago

Hmm, looks indeed like it is doing something wrong in the default Serde parser. I think this is because of the #[serde(flatten)].

Looks like we might need to add #[serde(skip_deserializing)] to https://github.com/GREsau/okapi/blob/master/okapi/src/openapi3.rs#L272:L273

But would have to look at it when I have some more time to be sure this solves it and does not create other problems. The pub extensions: Object, is part of more items, so a fix for 1 can then also be applied to all the other once that have a double #[serde(flatten)]. (PR is always welcome)

andrieshiemstra commented 2 years ago

For the moment i'm fixing this post-deser.. which for now is fine for me

Simply ignoring extensions when deserializing seems a bit drastic, so my first thought was to create a simple custom deserializer which ignores the fields already added to flattened ParameterValue enum..

But that got me thinking if this isn't actualy a bug in serde as i don't see how passing a deserialized field to two different flattened members will ever result in a valid structure when serializing again....