Altering schema during dispatching

Hello,
I’m creating a dynamic UI from generated JSON Schema coming from pydantic Python code.
Due to how pydantic works when I have an optional field it create this schema :

"name": {
    "anyOf": [
        {
            "type": "string"
        },
        {
            "type": "null"
        }
    ],
    "default": null,
    "title": "Name"
}

Out of the box JsonForms renders this kind of schema with a tabbed control which is not what I want. My plan is to create an OptionalRender that will alter the subschema to remove the “anyOf”/“null” part and keep only the non null object.

To do so I have created the following component :

function _OptionalRenderer(props: RendererProps) {
    let editedSchema = { ...props.schema } as JsonSchema;

    const nonNullType = editedSchema.anyOf?.find(obj => obj.type !== "null") as { type: string };
    delete editedSchema.anyOf;
    editedSchema = { ...editedSchema, ...nonNullType };

    return <JsonFormsDispatch {...props} schema={editedSchema} />;
}

export const OptionalRenderer = withJsonFormsControlProps(_OptionalRenderer);

With its tester :

const isOptional: Tester = (uischema, schema, context) => {
    if (!isAnyOfControl(uischema, schema, context))
        return false;
    const controlUISchmea = uischema as ControlElement;
    const resolved = resolveSchema(schema, controlUISchmea.scope, context.rootSchema);
    if (!resolved?.anyOf || resolved.anyOf.length !== 2)
        return false;
    return resolved.anyOf.some(obj => obj.type === "null");
}
const optionalFormTester = rankWith(4, isOptional);
const optionalRenderer = { tester: optionalFormTester, renderer: OptionalRenderer };

With this setup for the schema posted above its correctly rendering a unique TextControl as expected however, I get validation errors on the onChange callback of the JsonForms component.

Looking at the error :

{
  instancePath: "/name",
  schemaPath: "#/properties/name/anyOf/0/type",
  keyword: "type",
  params: {
    type: "string",
  },
  message: "must be string",
  schema: "string",
  parentSchema: {
    type: "string",
  },
  data: {
    name: "uu",
  },
}

I feel like the issue is because the data is an object with a name key rather than the plain text data. I don’t know why I get an object name:"uu" instead of just uu. I suspect that the issue comes from the schema alteration I’ve done but I can’t understand how to solve this issue.

If the solution I’ve found to alter the schema is a dead end I’m interested in other solution to solve my issue.

PS : I am looking for a general solution not a specific solution for this specific schema

Thanks !

Hi @Jojain,

Interesting use case! The problem here is that the withJsonFormsControlProps is basically called twice on mostly the same set of parameters.

One of them is the path parameter. The data path is modified to name via the first withJsonFormsControlProps of your custom optional renderer. But then you dispatch again, so the text control renderer will again run withJsonFormsControlProps. In it it will detect that it renders a name field, so it will append name to the path, resulting in name.name. And this is why the data is modified like this.

You can fix this by NOT wrapping your custom control in withJsonFormsControlProps, slightly adapt the code (as your schema will be the parent one) and then dispatch as you do.

Note that creating a new schema with every render call will force all children to also rerender whenever the custom renderer rerenders. If you want to prevent this you should wrap it in a useMemo.