jsonsystems / json-schema

JSONSchema.Net Public Repository
Apache License 2.0
663 stars 64 forks source link

Add support for multi-type arrays. #77

Closed josephspurrier closed 2 years ago

josephspurrier commented 4 years ago

Here is an example JSON file that should allow owners.groups to accept either an object or a string when I use the generator: https://jsonschema.net/home.

{
    "name": "power-user",
    "description": "Provides full access to AWS services and resources, but does not allow management of Users and groups.",
    "policy": "file://./power-user-policy.json",
    "owners": {
        "users": [
            {
                "idms": 1,
                "username": "admin"
            }
        ],
        "groups": [
            {
                "idms": 1,
                "name": "Administrators"
            },
            "file://./file-in-current-folder.json",
            "file://./../file-in-parent-folder.json"
        ]
    }
}

Currently, it generates a schema that only allows an object and then I have to manually change items from an object to an array and then add a 2nd item that looks like this:

{
    "$id": "#/properties/owners/properties/groups/items-string",
    "type": "string",
    "title": "The Items Schema",
    "description": "The paths should be relative to the current folder.",
    "default": "",
    "examples": [
        "file://./file-in-current-folder.json",
        "file://./../file-in-parent-folder.json"
    ]
}

By the way, awesome tool - very helpful! Everyone should be using schemas in their JSON and this is how they should be generating it.

josephspurrier commented 4 years ago

Actually - it looks like you need to support the use "oneOf" like this:

"transactions" : {
    "type" : "array",
    "items" : {
        "oneOf" : [
            {
                "type" : "object",
                "properties" : {
                    "type" : {
                        "type" : "string",
                        "enum" : ["BUILD", "REASSIGN"]
                    }
                }
            },
            {
               "type" : "object",
               "properties" : {
                 "type" : {
                   "type" : "string",
                   "enum" : ["BREAK"]
                  }
               }
            }
        ]
    }
}

https://stackoverflow.com/a/15673308

jackwootton commented 4 years ago

@josephspurrier Thanks for taking the time to post this issue. The current implementation could definitely be improved. First, I'm going to work with the following excerpt just to keep things simple:

{
  "groups": [
    {
      "idms": 1,
      "name": "Administrators"
    },
    "file://./file-in-current-folder.json"
  ] 
}

You're correct, only the first value of groups is used to infer the schema:

{
    "$schema": "http://json-schema.org/draft-07/schema",
    "type": "object",
    "properties": {
        "groups": {
            "type": "array",
            "examples": [
                [
                    {
                        "idms": 1.0,
                        "name": "Administrators"
                    },
                    "file://./file-in-current-folder.json"
                ]
            ],
            "items": {
                "type": "object",
                "examples": [
                    {
                        "idms": 1.0,
                        "name": "Administrators"
                    },
                    "file://./file-in-current-folder.json"
                ],
                "properties": {
                    "idms": {
                        "type": "integer",
                        "examples": [
                            1.0
                        ]
                    },
                    "name": {
                        "type": "string",
                        "examples": [
                            "Administrators"
                        ]
                    }
                }
            }
        }
    }
}

Using the settings it is possible to generate a schema for each item in the array:

{
    "$schema": "http://json-schema.org/draft-07/schema",
    "type": "object",
    "properties": {
        "groups": {
            "type": "array",
            "examples": [
                [
                    {
                        "idms": 1.0,
                        "name": "Administrators"
                    },
                    "file://./file-in-current-folder.json"
                ]
            ],
            "items": [
                {
                    "type": "object",
                    "examples": [
                        {
                            "idms": 1.0,
                            "name": "Administrators"
                        }
                    ],
                    "properties": {
                        "idms": {
                            "type": "integer",
                            "examples": [
                                1.0
                            ]
                        },
                        "name": {
                            "type": "string",
                            "examples": [
                                "Administrators"
                            ]
                        }
                    }
                },
                {
                    "type": "string",
                    "examples": [
                        "file://./file-in-current-folder.json"
                    ]
                }
            ]
        }
    }
}

List Validation = A single schema for all items Tuple Typing = Each item gets its own schema

image

jackwootton commented 4 years ago

WRT to a better solution I see a few options:

  1. OneOf
  2. AnyOf
  3. A union schema

oneOf - you've already explained how that could work.

anyOf - in your specific case, since groups can contain both strings and objects, this seems like a better solution (in this specific scenario)

A union schema (my own wording) could make use of the fact that type can be an array:

{
                    "type": ["object", "string"],
                    "examples": [
                        {
                            "idms": 1.0,
                            "name": "Administrators"
                        },
                       "file://./file-in-current-folder.json"
                    ],
}

The tricky part, is how to make all these options available (and intuitive) in the UI?

josephspurrier commented 4 years ago

Ah great catch. I should be using anyOf. Thanks for the suggestion. And the tuple option saves me a lot of time where I don't have to create the other objects, thanks for showing me how to do that. Could you add checkboxes for each of those in the UI. Do you think the union schema would be used often?

jackwootton commented 4 years ago

@josephspurrier I've updated www.jsonschema.net with some basic anyOf inference. Go to settings and set "Array Validation" to "AnyOf (single schema)".

This will generate a subschema for each type (integer, number, boolean, string, null, array, object).

If you could give it a go and let me know if there are any improvements that would make it more useful?

jackwootton commented 4 years ago

@josephspurrier Is this now working as you intended?

jackwootton commented 2 years ago

This repo is now readonly. If this issue remains relevant please open a new issue at https://github.com/jsonsystems/public