Relevant discussion:
Examples of current use:
Typically, inheritance is modelled something like this:
There are no inherent relationships between the data types these schemas describe, and a codegen could simply create three disparate classes, each with all of the required properties. However, that's not the intent behind the schemas. In order for that intent to be more explicitly expressed while still allowing for normal validation to occur, I propose we add an new annotation keyword that a schema should represent a "derived" type.
derived is a boolean that directs codegen tools to consider in-place references that appear either on their own (sibling $ref) or in an allOf as base types.
The above would change to
Without this keyword, or with a value of false, codegen will be directed to create disparate types.
Multiple inheritance
If we had a schema that had multiple references in an allOf, each reference would be a base type.
{
"$id": "derived-3",
"allOf": [
{ "$ref": "base" },
{ "$ref": "extension-data" }
],
"derived": true,
"properties": {
"baz": { "type": "boolean" }
},
"required": ["bar"]
}
for some extension-data schema.
This, however, requires multiple inheritance, which represents a problem for many languages. For example, JavaScript and .Net don't support multiple inheritance, whereas C++ and Python do. The workaround for those that don't may be to render the base types as interfaces (i.e. type definitions with no implementation). A "derived" class can then implement any number of these interfaces. You still get the polymorphism, but you don't get a class hierarchy.
For example, in C#, derived-3 may be generated as
interface IBase
{
string Foo { get; set; }
}
interface IExtensionData
{
// ... extension data
}
class Derived3 : IBase, IExtensionData
{
public string Foo { get; set; }
// ... extension data
}
Derived3 can be used anywhere an IBase or IExtensionData could be used, and any JSON instance that validates against derived-3 also validates against base and extension-data, so we have polymorphism in that respect.
However if we need to instantiate an IBase, we'd also need to create a class Base that implements IBase, and that Base class would not be polymorphic with Derived3.
It will need to be up to the tool to discern when to create base classes vs base interfaces as required by the generated language.
(This also illustrates why the JSON Schema team has historically recommended that generative logic should only be used as a developer tool, not in production. Generative logic cannot cater to every scenario, and any generated code should be verified before it's used.)
Other subschemas within an allOf
This issue only covers $ref schemas inside allOf. Subschemas could be handled as either as new types to be "inherited" from or merely as additional definition on the current type.
I've opened a separate issue for this since how to handle subschemas ties in with simple objects (#46).
Allowing undeclared properties
Many comments on this topic seem to want to apply additionalProperties to the base. But this is wrong. It would mean that a derived-* is not a base. But in terms of inheritance, a derived-* is a base. Leaving additionalProperties off solves the problems that arise when it's there.
Relevant discussion:
Examples of current use:
Typically, inheritance is modelled something like this:
There are no inherent relationships between the data types these schemas describe, and a codegen could simply create three disparate classes, each with all of the required properties. However, that's not the intent behind the schemas. In order for that intent to be more explicitly expressed while still allowing for normal validation to occur, I propose we add an new annotation keyword that a schema should represent a "derived" type.
derivedis a boolean that directs codegen tools to consider in-place references that appear either on their own (sibling$ref) or in anallOfas base types.The above would change to
Without this keyword, or with a value of
false, codegen will be directed to create disparate types.Multiple inheritance
If we had a schema that had multiple references in an
allOf, each reference would be a base type.{ "$id": "derived-3", "allOf": [ { "$ref": "base" }, { "$ref": "extension-data" } ], "derived": true, "properties": { "baz": { "type": "boolean" } }, "required": ["bar"] }for some
extension-dataschema.This, however, requires multiple inheritance, which represents a problem for many languages. For example, JavaScript and .Net don't support multiple inheritance, whereas C++ and Python do. The workaround for those that don't may be to render the base types as interfaces (i.e. type definitions with no implementation). A "derived" class can then implement any number of these interfaces. You still get the polymorphism, but you don't get a class hierarchy.
For example, in C#,
derived-3may be generated asDerived3can be used anywhere anIBaseorIExtensionDatacould be used, and any JSON instance that validates againstderived-3also validates againstbaseandextension-data, so we have polymorphism in that respect.However if we need to instantiate an
IBase, we'd also need to create a classBasethat implementsIBase, and thatBaseclass would not be polymorphic withDerived3.It will need to be up to the tool to discern when to create base classes vs base interfaces as required by the generated language.
(This also illustrates why the JSON Schema team has historically recommended that generative logic should only be used as a developer tool, not in production. Generative logic cannot cater to every scenario, and any generated code should be verified before it's used.)
Other subschemas within an
allOfThis issue only covers
$refschemas insideallOf. Subschemas could be handled as either as new types to be "inherited" from or merely as additional definition on the current type.I've opened a separate issue for this since how to handle subschemas ties in with simple objects (#46).
Allowing undeclared properties
Many comments on this topic seem to want to apply
additionalPropertiesto the base. But this is wrong. It would mean that aderived-*is not abase. But in terms of inheritance, aderived-*is abase. LeavingadditionalPropertiesoff solves the problems that arise when it's there.