Kiota API models
The primary goal of Kiota's generated models is to enable a developer to easily craft request payloads and get results as high levels objects. These models eliminate the need for developers to define their own types and/or implement serialization/deserialization.
All models declared as components are generated under a models
subnamespace. If models/components are namespaced components/modelA
, components/ns1/modelB
, the namespaces are included as subnamespaces of the models
namespace.
All models declared inline with an operation are generated under the namespace of the operation, next to the request builder that uses it. As a reminder, all request builders are put in namespaces according to the path segment they refer to.
Models in an allOf schema declaration inherit from each other. The uppermost type in the collection is the greatest ancestor of the chain.
The following table describes how kiota will project any schema with allOf entries when processing an API description:
Number of peer properties | Number of inline schemas | Number referenced schemas | Total number of schemas | Result |
---|---|---|---|---|
0 | 0 | 0 | 0 | Ignored/Invalid |
0 | 0 or 1 | 0 or 1 | 1 | Ignored, will process the only allOf entry instead and use the original schema's description and name. |
1 or more | 0 | 0 | 0 | Class/interface without a parent type. |
0 | 1 or more | 1 | * | Class/interface with merged properties from the inline schemas and a parent type from the referenced schema. |
1 or more | 0 | 1 | 1 | Class/interface with properties from the current schema and a parent type from the referenced schema. |
1 or more | 1 | 0 | 1 | Class/interface with properties from the current schema and a parent type from the inline schema. |
0 | 0 | 1 | 1 | Class/interface with properties from the referenced schema and without a parent type. |
0 | 1 | 0 | 1 | Class/interface with properties from the inline schema and without a parent type. |
1 or more | 1 | 1 | 2 | If the referenced schema has properties and the inline schema does not, class/interface with properties from the current schema and a parent type from referenced schema. |
1 or more | 1 | 1 | 2 | If the inline schema has properties and the referenced schema does not. class/interface with properties from the current schema and a parent type from the inline schema. |
1 or more | 1 | 1 | 2 | If both the inline schema and the referenced schema have properties: class/interface with properties from the current schema merged with the inline schema and a parent type from the referenced schema. |
1 or more | 1 | 1 | 2 | If none of the inline and the referenced schema have properties. Class/interface without a parent type. |
* | 1 or more | 0 or 1 | * | Class/interface with properties from the main schema, and allOf entries merged. |
* | 0 or 1 | 1 or more | * | Class/interface with properties from the main schema, and allOf entries merged. |
Note
These rules are applied to allOf entries recursively enabling multi-level inheritance.
oneOf
specifies a type union (exclusive) where the response can be of one of the specified child schemas. Kiota implements that specification by generating types for all the child schemas and using a union type for languages that support it or a wrapper type with one property per type in the union.
The deserialized result is either one of the types of the union or the wrapper type with only one of the properties being non-null.
When a oneOf
keyword has at least one child schema that is of type object then the OpenAPI discriminator keyword MUST be provided to identify the applicable schema.
Child schemas that are arrays or primitives use the equivalent type language parser to attempt to interpret the input value. The first primitive schema that doesn't fail to parse is used to deserialize the input.
Nested oneOf
keywords are only supported when the child schema uses a $ref
to enable naming the nested type.
anyOf
specifies a type intersection (inclusive union) where the response can be of any of the specified child schemas. Kiota implements that specification by generating types for all the child schemas and using an intersection type for languages that support it or a wrapper type with one property per type in the union.
The deserialized result is either an intersection type or the wrapper type with one or more of the properties being non-null.
Where there are common properties in the child schemas, the corresponding value in the input is deserialized into the first child schema with the common properties.
For any collection of items that rely on allOf
, anyOf
, or oneOf
, it's possible the result contains multiple types of objects.
For example, think of an endpoint returning a collection of directory objects (abstract). Directory object is derived by User and Group, and each type has its own set of properties. In this case, the endpoint is documented as returning a collection of directory objects and return in reality a mix of users and groups.
Kiota supports discriminators by down-casting the returned object during deserialization. The down-casting is supported by using allOf
and a discriminator property. Kiota supports both implicit and explicit discriminator mappings. Using oneOf
to constrain derived types is not supported as Kiota interprets that as an intersection type.
For inline schemas, the type name follows these conventions.
- Last segment name + operation + requestBody
- Last segment name + operation + response
- Parent type name + member + ID (sequential)
{
"microsoft.graph.directoryObject": {
"allOf": [
{ "$ref": "#/components/schemas/microsoft.graph.entity" },
{
"title": "directoryObject",
"required": ["@odata.type"],
"type": "object",
"properties": {
"@odata.type": {
"type": "string",
"default": "#microsoft.graph.directoryObject"
}
}
}
],
"discriminator": {
"propertyName": "@odata.type",
"mapping": {
"#microsoft.graph.user": "#/components/schemas/microsoft.graph.user",
"#microsoft.graph.group": "#/components/schemas/microsoft.graph.group"
}
}
},
"microsoft.graph.user": {
"allOf": [
{ "$ref": "#/components/schemas/microsoft.graph.directoryObject" },
{
"title": "user",
"type": "object"
}
]
},
"microsoft.graph.group": {
"allOf": [
{ "$ref": "#/components/schemas/microsoft.graph.directoryObject" },
{
"title": "group",
"type": "object"
}
]
}
}
{
"type": "object",
"title": "directoryObject",
"oneOf": [
{
"type": "object",
"title": "user"
},
{
"type": "object",
"title": "group"
}
]
}
In addition to all the described properties, a model contains a set of default members.
The factory method (static) is used by the parse node implementation to get the base or derived instance according to the discriminator value. Consider an operation that describes returning a Person
model, and the Person
model has discriminator information (mapping + property name). If the response payload contains one of the mapped values (for example, Employee
), the deserializer generates the derived Employee
type instead of the base Person
type. This way API client users can take advantage of the properties that are defined on this specialized model.
public static new Person CreateFromDiscriminatorValue(IParseNode parseNode) {
_ = parseNode ?? throw new ArgumentNullException(nameof(parseNode));
var mappingValueNode = parseNode.GetChildNode("@odata.type");
var mappingValue = mappingValueNode?.GetStringValue();
return mappingValue switch {
"#api.Employee" => new Employee(),
_ => new Person(),
};
}
The field deserializers method or property contains a list of callbacks to be used by the parse node implementation when deserializing the objects. Kiota relies on automatic serialization, where each type knows how to serialize/deserialize itself thanks to the OpenAPI description. A significant advantage of this approach it to avoid tying the generated models to any specific serialization format (JSON, YAML, XML,...) or any specific library (because of attributes/annotations these libraries often require).
Note
Any property found in the response payload and which does not match an entry in the field deserializers (casing included), will be stored in the additional data collection.
Like the field deserializers, the model's serialize method uses the passed serialization writer to serialize itself.
Dictionary/Map that stores all the additional properties that aren't described in the schema.
Note
The additional data property is only present when the OpenAPI description for the type allows it and if the current model doesn't inherit a model which already has this property.
When present, the property values are stored in this backing store instead of using fields for the object. The backing store allows multiple things like dirty tracking of changes, making it possible to get an object from the API, update a property, and send that object back with only the changed property and not the full object. Additionally it's used for integration with custom data sources.
Note
The backing store is only added if the target language supports it and when the -b
parameter is passed to the CLI when generating the models.
To produce a more idiomatic output for specific languages, mangling is applied to the properties names and/or accessors. The following table describes the mangling rules being applied for each language:
Language | Property name | Property accessors |
---|---|---|
CSharp | PascalCase |
- |
CLI | PascalCase |
- |
Go | - | PascalCase |
Java | camelCase |
camelCase |
PHP | - | camelCase |
Python | snake_case |
snake_case |
Ruby | snake_case |
snake_case |
Swift | - | - |
TypeScript | - | camelCase |
Kiota only projects enum models when the schema type is string, as projecting enums for boolean or number types doesn't add much value.
Additionally, you can:
- Control the enum symbol name using the x-ms-enum extension. more information
- Control whether the projected enum should be flaggable through the x-ms-enum-flags extension. more information
During deserialization, if an unknown enum member value is encountered, the client throws an exception. This design decision was made to ensure client applications fail as early as possible when the client might need to be refreshed or when the API description is inaccurate.