Skip to content

Better support for custom schemas #552

@FrancescoMerenda

Description

@FrancescoMerenda

I am exploring using this library to generate Zod Schemas (which is a Typescript validation library).
The current JSON Schema generation implementation was a great starting point, but I've encountered some limitations particularly regarding tagged unions and enum representations.

The main issues I found are the following:

  • Zod is not a format but only a schema so I had to create an empty Reader and Writer;
  • rfl::TaggedUnions are converted to rfl::parsing::schema::Type::Anyof (losing type information);
  • enums are always interpreted as rfl::parsing::schema::Type::Integer (if your parser is Schemaful) or StringEnum;

This could be a sketch of a potential API:

  • TaggedUnions with StringEnums
// Tagged unions with enums
enum class ShapesTags{
  Circle,
  Rectangle,
  Square,
};
struct Circle {
  // here we could make a specialization of rfl::Literal
  // that convert the enum case to the string 
  using Tag = rfl::Literal<ShapesTags::Circle>;
  double radius;
};

struct Rectangle {
  using Tag = rfl::Literal<ShapesTags::Rectangle>;
  double height;
  double width;
};

struct Square {
  using Tag = rfl::Literal<ShapesTags::Square>;
  double width;
};

using Shapes = rfl::TaggedUnion<"shape", Circle, Square, Rectangle>;
  • TaggedUnions with IntegerEnums
struct Circle {
  // we should use a NumberLiteral which can be also useful
  // for other stuff and makes this change backward-compatible
  using Tag = rfl::NumberLiteral<ShapesTags::Circle>;
  double radius;
};

struct Rectangle {
  using Tag = rfl::NumberLiteral<ShapesTags::Rectangle>;
  double height;
  double width;
};

struct Square {
  using Tag = rfl::NumberLiteral<ShapesTags::Square>;
  double width;
};

using Shapes = rfl::TaggedUnion<"shape", Circle, Square, Rectangle>;
  • using Enums underlying value
enum class ShapesEnum{
  Circle,
  Rectangle,
  Square,
};
 
// this could be used to set flags for further options
// could also be aliased to apply the same options when you use this
using Shapes = rfl::Enum<ShapesEnum>;
struct MyType {
   Shapes shape;
}
  • adding TaggedUnion to schema types
struct IntegerTagged {
    std::string field_;
    std::vector<std::pair<int, rfl::parsing::schema::Type>> types_;
  };
struct StringTagged {
    std::string field_;
    std::vector<std::pair<std::string, rfl::parsing::schema::Type>> types_;
  };

The problem rfl::Enum would solve cannot be solved with a simple ReflectionType to an int because:

  • we lose the constraints on valid enum values
  • we lose the schema enum definition entirely

Also, the problem is not solved by the preprocessor flag rfl::UnderlyingEnum because when generating a schema you may have many enums in one structure with different flags needed.

How an enum should be (de)serialized should be a property of that type (but this is just my opinion so I can understand others may not agree).

For now I was able to achieve something like this without editing the sources by making specific template specializations to my rfl::zod::Parser but I feel like those basic ideas would be useful to the library.

I would gladly contribute with some guidance if the proposal is accepted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions