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

Generic types in regular mode aren't deduplicated in schema definitions #297

Open
mrdziuban opened this issue Jan 31, 2023 · 2 comments
Open
Assignees

Comments

@mrdziuban
Copy link

Describe the bug

Using a generic type, i.e. one with a type parameter, multiple times in a single class causes an invalid schema to be generated. There is only one definition generated for the type parameter, but it's referenced from both of the other definitions that refer to the type parameter.

To Reproduce

https://scastie.scala-lang.org/UfJUqfSmTvKu3nr0ImJfxA

import json.{Json, Schema}
import json.schema.Version

case class Foo[A](data: A)
implicit def jsonSchemaFoo[A: Schema] = Json.schema[Foo[A]]

case class Bar(x: Int)
implicit val jsonSchemaBar = Json.schema[Bar]

case class Baz(y: String)
implicit val jsonSchemaBaz = Json.schema[Baz]

case class Quux(foobar: Foo[Bar], foobaz: Foo[Baz])

println(Json.stringify(
  Json.schema[Quux],
  Version.Draft07("Quux"),
))

This produces

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "Quux",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "foobar": {
      "$ref": "#Playground.Foo[Playground.Bar]"
    },
    "foobaz": {
      "$ref": "#Playground.Foo[Playground.Baz]"
    }
  },
  "required": [
    "foobar",
    "foobaz"
  ],
  "definitions": {
    "Playground.A": {
      "$id": "#Playground.A",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "y": {
          "type": "string"
        }
      },
      "required": [
        "y"
      ]
    },
    "Playground.Foo[Playground.Bar]": {
      "$id": "#Playground.Foo[Playground.Bar]",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "data": {
          "$ref": "#Playground.A"
        }
      },
      "required": [
        "data"
      ]
    },
    "Playground.Foo[Playground.Baz]": {
      "$id": "#Playground.Foo[Playground.Baz]",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "data": {
          "$ref": "#Playground.A"
        }
      },
      "required": [
        "data"
      ]
    }
  }
}

Expected behavior

There should be four definitions in the resulting schema:

  • Playground.Bar
  • Playground.Baz
  • Playground.Foo[Bar] that references Playground.Bar
  • Playground.Foo[Baz] that references Playground.Baz

Actual results

The definitions for Playground.Foo[Bar] and PlayGround.Foo[Baz] both reference Playground.A (the name of Foo's type parameter), but the definition for Playground.A only contains the fields of Baz -- the correct definition of Bar is missing -- so the schema doesn't match the structure of the data.

Versions:

  • jsonschema 0.7.9
  • scala version 2.13.10
@andyglow
Copy link
Owner

this is a very interesting case
may require some more debugging
for now i'd recommend to not use implicit def for schemas.
i'm not sure if this would cover your case but what would help in the given scenario is

implicit val jsonSchemaFooBar = Json.schema[Foo[Bar]]
implicit val jsonSchemaFooBaz = Json.schema[Foo[Baz]]

instead of

implicit def jsonSchemaFoo[A: Schema] = Json.schema[Foo[A]]

@andyglow andyglow added enhancement and removed bug labels Jun 28, 2023
@andyglow
Copy link
Owner

in general statement like

implicit def jsonSchemaFoo[A: Schema] = Json.schema[Foo[A]]

simply won't work

this statement can be decoded like this

implicit def jsonSchemaFoo[A](implicit aSchema: Schema[A]] = Json.schema[Foo[A]]

as you can see - Json.schema doesn't not accept aSchema.. so the underlying macro have no idea about the A type, not it knows or want to know about aSchema

so basic answer would be - no this won't work
but on the other hand it specifies a nice problem.
I will try to find some time to think about it

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

No branches or pull requests

2 participants