Description
What are you generating using Kiota, clients or plugins?
API Client/SDK
In what context or format are you using Kiota?
Nuget tool
Client library/SDK language
TypeScript
Describe the bug
The TypeScript generator creates incorrect deserialization logic for an array of a polymorphic type when that array is a property of another model. Instead of using the discriminator mapping for each element in the collection, the generated code attempts to deserialize the entire collection into one of the possible types, and then tries the next type if the first attempt fails. This results in incorrectly deserialized objects within the array.
This issue does not occur when an endpoint returns the array of the polymorphic type directly. In that case, the discriminator logic is applied correctly.
I also generated the corresponding C# client for the OpenAPI yaml which deserialized the nested array correctly, so this appears to be typescript related.
As an example I have the following model (csharp, for better readability, api definition is attached via the link):
public class WeatherForecastSummary
{
public List<WeatherForecast> Forecasts { get; set; } = [];
}
[JsonPolymorphic(TypeDiscriminatorPropertyName = "weatherType")]
[JsonDerivedType(typeof(RainyDayForecast), "Rain")]
[JsonDerivedType(typeof(SunnyDayForecast), "Sun")]
public abstract class WeatherForecast
{
public DateOnly Date { get; set; }
}
public class RainyDayForecast : WeatherForecast
{
public int RainfallAmount { get; set; }
}
public class SunnyDayForecast : WeatherForecast
{
public int TemperatureC { get; set; }
}
This is the incorrect code generated for the WeatherForecastSummary model's deserialization:
export function deserializeIntoWeatherForecastSummary(weatherForecastSummary: Partial<WeatherForecastSummary> | undefined = {}): Record<string, (node: ParseNode) => void> {
return {
"forecasts": n => {
weatherForecastSummary.forecasts =
n.getCollectionOfObjectValues<WeatherForecastRainyDayForecast>(createWeatherForecastRainyDayForecastFromDiscriminatorValue)
?? n.getCollectionOfObjectValues<WeatherForecastSunnyDayForecast>(createWeatherForecastSunnyDayForecastFromDiscriminatorValue);
},
}
}
This code tries to deserialize the entire forecasts array as WeatherForecastRainyDayForecast[] and if that doesn't work, it tries WeatherForecastSunnyDayForecast[].
For comparison, when the response is a direct array of WeatherForecast, Kiota correctly generates a factory method that uses the discriminator:
export function createWeatherForecastFromDiscriminatorValue(parseNode: ParseNode | undefined) : ((instance?: Parsable) => Record<string, (node: ParseNode) => void>) {
const mappingValueNode = parseNode?.getChildNode("weatherType");
if (mappingValueNode) {
const mappingValue = mappingValueNode.getStringValue();
if (mappingValue) {
switch (mappingValue) {
case "Rain":
return deserializeIntoWeatherForecastRainyDayForecast;
case "Sun":
return deserializeIntoWeatherForecastSunnyDayForecast;
}
}
}
return deserializeIntoWeatherForecast;
}
export const SummaryRequestBuilderRequestsMetadata: RequestsMetadata = {
get: {
uriTemplate: SummaryRequestBuilderUriTemplate,
responseBodyContentType: "application/json",
adapterMethodName: "send",
responseBodyFactory: createWeatherForecastSummaryFromDiscriminatorValue,
},
};
Expected behavior
The deserialization logic for the forecasts property within deserializeIntoWeatherForecastSummary should leverage the createWeatherForecastFromDiscriminatorValue factory for each item in the collection, not for the collection as a whole. The generated code should look more like this:
// Conceptual correct implementation
export function deserializeIntoWeatherForecastSummary(weatherForecastSummary: Partial<WeatherForecastSummary> | undefined = {}): Record<string, (node: ParseNode) => void> {
return {
"forecasts": n => {
weatherForecastSummary.forecasts = n.getCollectionOfObjectValues(createWeatherForecastFromDiscriminatorValue);
},
}
}
How to reproduce
I created a minimal working example in the following repo with instructions on how to run it: https://github.com/nreinartz/kiota-ts-incorrect-derserialization
Open API description file
https://github.com/nreinartz/kiota-ts-incorrect-derserialization/blob/main/openapi.json
Kiota Version
1.26.1
Latest Kiota version known to work for scenario above?(Not required)
No response
Known Workarounds
No response
Configuration
No response
Debug output
Click to expand log
``` dbug: Kiota.Builder.KiotaBuilder[0] kiota version 1.26.1 info: Kiota.Builder.KiotaBuilder[0] loaded description from local source dbug: Kiota.Builder.KiotaBuilder[0] step 1 - reading the stream - took 00:00:00.0049053 dbug: Kiota.Builder.KiotaBuilder[0] step 2 - parsing the document - took 00:00:00.0554505 dbug: Kiota.Builder.KiotaBuilder[0] step 3 - updating generation configuration from kiota extension - took 00:00:00.0000644 dbug: Kiota.Builder.KiotaBuilder[0] step 4 - filtering API paths with patterns - took 00:00:00.0027317 info: Kiota.Builder.KiotaBuilder[0] Client root URL set to http://localhost:5000 dbug: Kiota.Builder.KiotaBuilder[0] step 5 - checking whether the output should be updated - took 00:00:00.0195813 dbug: Kiota.Builder.KiotaBuilder[0] step 6 - create uri space - took 00:00:00.0021569 dbug: Kiota.Builder.KiotaBuilder[0] InitializeInheritanceIndex 00:00:00.0021048 dbug: Kiota.Builder.KiotaBuilder[0] CreateRequestBuilderClass 00:00:00 dbug: Kiota.Builder.KiotaBuilder[0] MapTypeDefinitions 00:00:00.0026959 dbug: Kiota.Builder.KiotaBuilder[0] TrimInheritedModels 00:00:00 dbug: Kiota.Builder.KiotaBuilder[0] CleanUpInternalState 00:00:00 dbug: Kiota.Builder.KiotaBuilder[0] step 7 - create source model - took 00:00:00.0422758 dbug: Kiota.Builder.KiotaBuilder[0] 32ms: Language refinement applied dbug: Kiota.Builder.KiotaBuilder[0] step 8 - refine by language - took 00:00:00.0329552 dbug: Kiota.Builder.KiotaBuilder[0] step 9 - writing files - took 00:00:00.0248570 info: Kiota.Builder.KiotaBuilder[0] loaded description from local source dbug: Kiota.Builder.KiotaBuilder[0] step 10 - writing lock file - took 00:00:00.0051393 Generation completed successfully ```Other information
No response
Metadata
Metadata
Assignees
Labels
Type
Projects
Status