jsonsystems / json-schema

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

Enum Mishandling #104

Closed Haga14 closed 3 years ago

Haga14 commented 3 years ago

When generating schema for a json object with an enum property, among many other properties, such as the following:


{
    "user_Name": "",
    "name": "",
    "post_Type": ["lesson", "assignment", "discussion", "comment"],
    "quiz_Type": ["multiple choice", "multiple correct", "essay", "math"]
}

*NOTE: post_Type and quiz_Type are the enums

The tool generates an invalid schema such as this:



{
    "$schema": "http://json-schema.org/draft-07/schema",
    "$id": "http://example.com/example.json",
    "type": "object",
    "title": "The root schema",
    "description": "The root schema comprises the entire JSON document.",
    "default": {},
    "examples": [
        {
            "user_Name": "",
            "name": "",
            "post_Type": [
                "lesson",
                "assignment",
                "discussion",
                "comment"
            ],
            "quiz_Type": [
                "multiple choice",
                "multiple correct",
                "essay",
                "math"
            ]
        }
    ],
    "enum": [
        {
            "user_Name": "",
            "name": "",
            "post_Type": [
                "lesson",
                "assignment",
                "discussion",
                "comment"
            ],
            "quiz_Type": [
                "multiple choice",
                "multiple correct",
                "essay",
                "math"
            ]
        }
    ],
    "required": [
        "user_Name",
        "name",
        "post_Type",
        "quiz_Type"
    ],
    "properties": {
        "user_Name": {
            "$id": "#/properties/user_Name",
            "type": "string",
            "title": "The user_Name schema",
            "description": "An explanation about the purpose of this instance.",
            "default": "",
            "examples": [
                ""
            ],
            "enum": [
                ""
            ]
        },
        "name": {
            "$id": "#/properties/name",
            "type": "string",
            "title": "The name schema",
            "description": "An explanation about the purpose of this instance.",
            "default": "",
            "examples": [
                ""
            ],
            "enum": [
                ""
            ]
        },
        "post_Type": {
            "$id": "#/properties/post_Type",
            "type": "array",
            "title": "The post_Type schema",
            "description": "An explanation about the purpose of this instance.",
            "default": [],
            "examples": [
                [
                    "lesson",
                    "assignment"
                ]
            ],
            "enum": [
                [
                    "lesson",
                    "assignment",
                    "discussion",
                    "comment"
                ]
            ],
            "additionalItems": false,
            "items": {
                "$id": "#/properties/post_Type/items",
                "anyOf": [
                    {
                        "$id": "#/properties/post_Type/items/anyOf/0",
                        "type": "string",
                        "title": "The first anyOf schema",
                        "description": "An explanation about the purpose of this instance.",
                        "default": "",
                        "examples": [
                            "lesson",
                            "assignment"
                        ],
                        "enum": [
                            "lesson",
                            "assignment",
                            "discussion",
                            "comment"
                        ]
                    }
                ]
            }
        },
        "quiz_Type": {
            "$id": "#/properties/quiz_Type",
            "type": "array",
            "title": "The quiz_Type schema",
            "description": "An explanation about the purpose of this instance.",
            "default": [],
            "examples": [
                [
                    "multiple choice",
                    "multiple correct"
                ]
            ],
            "enum": [
                [
                    "multiple choice",
                    "multiple correct",
                    "essay",
                    "math"
                ]
            ],
            "additionalItems": false,
            "items": {
                "$id": "#/properties/quiz_Type/items",
                "anyOf": [
                    {
                        "$id": "#/properties/quiz_Type/items/anyOf/0",
                        "type": "string",
                        "title": "The first anyOf schema",
                        "description": "An explanation about the purpose of this instance.",
                        "default": "",
                        "examples": [
                            "multiple choice",
                            "multiple correct"
                        ],
                        "enum": [
                            "multiple choice",
                            "multiple correct",
                            "essay",
                            "math"
                        ]
                    }
                ]
            }
        }
    },
    "additionalProperties": false
}
jackwootton commented 3 years ago

@Haga14 Thanks for posting this issue. Can you add some detail around what is wrong with the generated schema?

Haga14 commented 3 years ago

@jsonsystems Thanks for the quick response. It treats the root object as well as every property of the root object as an enum, instead of the enum properties. If you look at my root object, it has some enum properties (namely <$>_Type properties). It should be treating just those as enums and it shouldn't touch any of the other properties.

I'm reporting this as an issue as my current understanding is that when enum is enabled, it should convert every array property with string values into an enum. If this is wrong, how would one generate a schema for an object that has enum properties among non-enum properties?

For Example: the currency_Types and vehicle_Types properties would be turned into an enum and all the other properties would be left as is:


{
    "currency_Types": ["US Dollar", "Euro", "Yen", "Pound"],
    "vehicle_Types": ["SUV", "Sedan", "Truck"],
    "Country": "",
    "name": "",
    "phone_Number": ""
}
Haga14 commented 3 years ago

@jsonsystems any update on this?

jackwootton commented 3 years ago

@Haga14 Sorry for the delay. AFAIK the enum keyword can be applied to any instance type, not just arrays. From v7 spec

6.1.2.  enum

   The value of this keyword MUST be an array.  This array SHOULD have
   at least one element.  Elements in the array SHOULD be unique.

   An instance validates successfully against this keyword if its value
   is equal to one of the elements in this keyword's array value.

   Elements in the array might be of any value, including null.

The keyword must be an array, but that does not mean it can only be applied to array instances. In your example, the enum keyword for the Country property could be

{
            "$id": "#Country",
            "type": "string",
            "default": "",
            "title": "The Country schema",
            "enum": ["Russia", "Canada", "United States", "China", "Brazil"]
}
Haga14 commented 3 years ago

RIght, it hands a case such as the countries array, when there is only one property in the root object, just fine. My issue is when countries is one among a number of properties in an object. For example:


{
    "name": "Africa",
    "population": 1.5,
    "populationMetic": "Billion",
    "countries": ["Kenya", "Rwanda", "Egypt", "Djbouti", "Somalia", "Tanzania"]

}

If I feed this object in, the following is what it generates, which is invalid as it treats every property as an enum, rather than just the countries property


{
    "$schema": "http://json-schema.org/draft-07/schema",
    "$id": "http://example.com/example.json",
    "type": "object",
    "title": "The root schema",
    "description": "The root schema comprises the entire JSON document.",
    "default": {},
    "examples": [
        {
            "name": "Africa",
            "population": 1.5,
            "populationMetic": "Billion",
            "countries": [
                "Kenya",
                "Rwanda",
                "Egypt",
                "Djbouti",
                "Somalia",
                "Tanzania"
            ]
        }
    ],
    "enum": [
        {
            "name": "Africa",
            "population": 1.5,
            "populationMetic": "Billion",
            "countries": [
                "Kenya",
                "Rwanda",
                "Egypt",
                "Djbouti",
                "Somalia",
                "Tanzania"
            ]
        }
    ],
    "required": [
        "name",
        "population",
        "populationMetic",
        "countries"
    ],
    "properties": {
        "name": {
            "$id": "#/properties/name",
            "type": "string",
            "title": "The name schema",
            "description": "An explanation about the purpose of this instance.",
            "default": "",
            "examples": [
                "Africa"
            ],
            "enum": [
                "Africa"
            ]
        },
        "population": {
            "$id": "#/properties/population",
            "type": "number",
            "title": "The population schema",
            "description": "An explanation about the purpose of this instance.",
            "default": 0.0,
            "examples": [
                1.5
            ],
            "enum": [
                1.5
            ]
        },
        "populationMetic": {
            "$id": "#/properties/populationMetic",
            "type": "string",
            "title": "The populationMetic schema",
            "description": "An explanation about the purpose of this instance.",
            "default": "",
            "examples": [
                "Billion"
            ],
            "enum": [
                "Billion"
            ]
        },
        "countries": {
            "$id": "#/properties/countries",
            "type": "array",
            "title": "The countries schema",
            "description": "An explanation about the purpose of this instance.",
            "default": [],
            "examples": [
                [
                    "Kenya",
                    "Rwanda"
                ]
            ],
            "enum": [
                [
                    "Kenya",
                    "Rwanda",
                    "Egypt",
                    "Djbouti",
                    "Somalia",
                    "Tanzania"
                ]
            ],
            "additionalItems": true,
            "items": {
                "$id": "#/properties/countries/items",
                "anyOf": [
                    {
                        "$id": "#/properties/countries/items/anyOf/0",
                        "type": "string",
                        "title": "The first anyOf schema",
                        "description": "An explanation about the purpose of this instance.",
                        "default": "",
                        "examples": [
                            "Kenya",
                            "Rwanda"
                        ],
                        "enum": [
                            "Kenya",
                            "Rwanda",
                            "Egypt",
                            "Djbouti",
                            "Somalia",
                            "Tanzania"
                        ]
                    }
                ]
            }
        }
    },
    "additionalProperties": true
}

In a case like this, how does one properly use the generator so that the countries array is handled as an enum, but the rest as number and string respectively?

It would be helpful if there was a guide on how to use the tool and even better if the code was public. I would've fixed the issue and made a pull request by now