react-jsonschema-form icon indicating copy to clipboard operation
react-jsonschema-form copied to clipboard

Nested oneOf/anyOf not rendered

Open phemmer opened this issue 5 months ago • 3 comments

Prerequisites

What theme are you using?

antd

Version

5.x

Current Behavior

This is a bit of a mix of a bug report and a feature request. A bug report in that a nested oneOf/anyOf don't work, and a feature request to make them work in a certain way.

The issue is that currently RJSF does not handle nested oneOf/anyOf lists. When doing so, the nested list fails to render in any way. So making this functionality work at all is the bug report.

Expected Behavior

The simple way of doing it would be to render the first drop down, and then when the option for the nested list is selected, to render another drop down.

The feature request is to instead allow merging/concatenating nested lists. For example if you have a oneOf inside a oneOf, to allow merging of all the options as if they were a single oneOf list.

Steps To Reproduce

{
  "type": "object",
  "properties": {
    "myprop": {
      "oneOf": [
        {
          "title": "none",
          "type": "object"
        },
        {
          "$ref": "#/$defs/some"
        },
        {
          "$ref": "#/$defs/mylist"
        }
      ]
    }
  },
  "$defs": {
    "some": {
      "title": "some",
      "const": "some"
    },
    "mylist": {
      "oneOf": [
        {
          "const": "foo"
        },
        {
          "const": "bar"
        }
      ]
    }
  }
}

playground

In the above, if you select "Option 3", there is no drop-down presented to select foo or bar. However the following are all valid against the schema:

{"myprop": {}}
{"myprop": "some"}
{"myprop": "foo"}
{"myprop": "bar"}

Environment

- OS:
- Node:
- npm:

Anything else?

As a side note, one of the main reasons for this was I wanted to prevent RJSF from automatically selecting the first item in the oneOf list (for myprop) and rendering it. And all the options for the list are defined in $defs. "none" isn't meant to be a valid option in the list, and is only applicable to the property, so I didn't want to put it in the list itself, and instead keep it on the property definition. Thus the property with a oneOf containing none, and then a reference to the real list.

However that said, it is not my only use case for this functionality. I also have other lists, where sometimes I want to only allow a subset of options, and other times I want to allow all options. For example think of a country selector. You might have a list in $defs for all the countries in Europe, and another list for North America. Sometimes you only want to allow selecting the European countries, but sometimes you want to allow selecting all countries. So the solution would be to define an "all" list, which contains references to each of the continental lists.

phemmer avatar Jun 11 '25 16:06 phemmer

@phemmer You're missing "type" properties, so RJSF has no idea how to render these nested oneOf fields. If you annotate your subschemas with "type", then the fields properly render.

Here's a fixed playground.

Is this closer to what you had in mind?

nickgros avatar Jun 13 '25 19:06 nickgros

@phemmer A slight modification to the schema that @nickgros provided will allow the none choice to not have an input field. And when you select it an submit you are given an error

Here the alternate playground

heath-freenome avatar Jun 13 '25 19:06 heath-freenome

That explanation doesn't seem to hold up. Few reasons:

  1. The first oneOf doesn't have a type either, yet it renders fine.
  2. What if you want your items to have different types?
  3. I didn't notice at first, but the problem actually seems to be restricted to having options of the same type. For example this works fine, while this doesn't. This doesn't make sense to me. Why would the first two items work when the third is present, but then when you remove the third, those first two which were working fine no longer do?

phemmer avatar Jun 13 '25 22:06 phemmer

That explanation doesn't seem to hold up. Few reasons:

  1. The first oneOf doesn't have a type either, yet it renders fine.
  2. What if you want your items to have different types?
  3. I didn't notice at first, but the problem actually seems to be restricted to having options of the same type. For example this works fine, while this doesn't. This doesn't make sense to me. Why would the first two items work when the third is present, but then when you remove the third, those first two which were working fine no longer do?

@phemmer If you add type to the second example like this It works. So adding the type property is important and is consistent with what @nickgros mentioned.

Barring an example that shows a real bug, I expect this will be closed soon

heath-freenome avatar Jun 20 '25 20:06 heath-freenome

Yes, I understood that if you put a type outside of the oneOf definition it works. That's exactly what all three of the points I mentioned are about.

phemmer avatar Jun 20 '25 21:06 phemmer

Ok, so basically to summarize, today RJSF can handle the schema (playground)

{ "const": "someString" }

but it cannot handle the schema (playground)

{
  "oneOf": [
    {
      "const": "someString"
    }
  ]
}

A workaround exists where if you provide the type, it works (playground)

{
  "type": "string",
  "oneOf": [
    {
      "const": "someString"
    }
  ]
}

The oneOf case without the type is technically a valid JSON schema, so we could probably support it, but since a workaround exists, this is probably pretty low on our priority list. We would be happy to review and merge a PR from @phemmer or any other community member interested in investigating and providing a fix. Thanks.

nickgros avatar Jun 27 '25 19:06 nickgros