Home > Net >  JsonSchema for struct to use string representation of struct rather than the struct fields
JsonSchema for struct to use string representation of struct rather than the struct fields

Time:09-22

Here is my struct

#[derive(Serialize, Deserialize, Clone, Copy, Debug, JsonSchema)]
pub struct Position {
    x: SignedDecimal,
    y: SignedDecimal,
}

#[derive(Serialize, Deserialize, Clone, Copy, Debug, JsonSchema)]
pub struct SignedDecimal {
    value: Decimal256,
    sign:  bool,
}

An example output of the default schema looks like this:

{
    "x": { "value": "1.5", "sign": true },
    "y": { "value": "1.5", "sign": true }
}

What I want it to look like is this:

{
    "x": "1.5",
    "y": "1.5"
}

But I can't seem to get the Schemars macros to do what I want. I've tried many things, like this for example:

#[derive(Serialize, Deserialize, Clone, Copy, Debug, JsonSchema)]
#[schemars(schema_with = "String")]
pub struct SignedDecimal {
    value: Decimal256,
    sign:  bool,
}

Ideally I am not using any additional macros on the Position struct, and instead just modifying the SignedDecimal struct. All of the relevant String impls have been defined, I just can't make the macro do what I want.

CodePudding user response:

You could overwrite the schema for fields with #[schemars(schema_with = "…")]. For example:

#[derive(Serialize, Deserialize, Clone, Copy, Debug, JsonSchema)]
pub struct Position {
    #[schemars(schema_with = "pretend_to_be_number_string")]
    x: SignedDecimal,
    #[schemars(schema_with = "pretend_to_be_number_string")]
    y: SignedDecimal,
}

fn pretend_to_be_number_string(
    gen: &mut schemars::gen::SchemaGenerator,
) -> schemars::schema::Schema {
    use schemars::schema::*;
    let mut obj = SchemaObject::default();
    obj.instance_type = Some(SingleOrVec::Single(InstanceType::String.into()));
    obj.string().pattern = Some("[ -]?[0-9]*\\.[0-9]*".into()); // TODO: rudimentary
    let obj = Schema::Object(obj);
    gen.definitions_mut()
        .insert("SignedDecimal".into(), obj.clone());
    let mut or = SchemaObject::default();
    or.reference = Some("#/definitions/SignedDecimal".into());
    Schema::Object(or)
}

will result in the schema

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Position",
  "type": "object",
  "required": [
    "x",
    "y"
  ],
  "properties": {
    "x": {
      "$ref": "#/definitions/SignedDecimal"
    },
    "y": {
      "$ref": "#/definitions/SignedDecimal"
    }
  },
  "definitions": {
    "SignedDecimal": {
      "type": "string",
      "pattern": "[ -]?[0-9]*\\.[0-9]*"
    }
  }
}

There is probably a way of not having to specify this on every field, but I can't find it.

CodePudding user response:

Not a perfect solution but I'm going to manually implement JsonSchema since I can't figure out how to make it work with the provided macros.

impl JsonSchema for SignedDecimal {
    fn schema_name() -> String {
        "signed_decimal".to_string()
    }

    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
        String::json_schema(gen)
    }

    fn is_referenceable() -> bool {
        true
    }
}
  • Related