microsoft / microsoft/kiota

C# Model - oneOf of list and object is not deserialized correctly

Open
#3,976 19 comments 1 reaction 1 assignee Assigned to @Irame View on GitHub
Csharp Needs: Attention :wave: type:question
Dominant language
C#
Stars
3.8k
Forks
333
Avg merge
16h 29m
Merged PRs (30d)
116

Description

I want to consume an API that has the following construct for properties (`OneOrManyItems` in this example) at quite a lot of places:

```json
{
"openapi": "3.0.3",
"info": {
"title": "Test",
"version": ""
},
"paths": {
"/test": {
"get": {
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Result"
}
}
}
},
"401": {
"description": "Unauthorized Request"
}
}
}
}
},
"components": {
"schemas": {
"Result": {
"properties": {
"OneOrManyItems": {
"oneOf": [
{
"type": "array",
"items": {
"$ref": "#/components/schemas/Item"
}
},
{
"$ref": "#/components/schemas/Item"
}
]
}
}
},
"Item": {
"properties": {
"Id": {
"type": "string"
}
}
}
}
}
}
```

Its basically a property (`OneOrManyItems`) that can be a list of objects or just one object (both with the same type `Item`).

When I now generate a client using this command: `kiota generate -d desciption.json -l CSharp`.
I get a model for the result that does not parse the json response correctly. It only parses it correctly for the case that it is a list of items not a single item.

The `Result_OneOrManyItems` class inside the `Result` looks something like this:
```cs
public class Result_OneOrManyItems : IComposedTypeWrapper, IParsable {
public List? Item { get; set; }
public ApiSdk.Models.Item? ResultOneOrManyItemsItem { get; set; }

public static Result_OneOrManyItems CreateFromDiscriminatorValue(IParseNode parseNode) {
_ = parseNode ?? throw new ArgumentNullException(nameof(parseNode));
var mappingValue = parseNode.GetChildNode("")?.GetStringValue();
var result = new Result_OneOrManyItems();
if("Item".Equals(mappingValue, StringComparison.OrdinalIgnoreCase)) {
result.ResultOneOrManyItemsItem = new ApiSdk.Models.Item();
}
else if(parseNode.GetCollectionOfObjectValues(ApiSdk.Models.Item.CreateFromDiscriminatorValue)?.ToList() is List itemValue) {
result.Item = itemValue;
}
return result;
}

public virtual IDictionary> GetFieldDeserializers() {
if(ResultOneOrManyItemsItem != null) {
return ResultOneOrManyItemsItem.GetFieldDeserializers();
}
return new Dictionary>();
}

public virtual void Serialize(ISerializationWriter writer) {
_ = writer ?? throw new ArgumentNullException(nameof(writer));
if(ResultOneOrManyItemsItem != null) {
writer.WriteObjectValue(null, ResultOneOrManyItemsItem);
}
else if(Item != null) {
writer.WriteCollectionOfObjectValues(null, Item);
}
}
}
```
When deserialized the `ResultOneOrManyItemsItem` property is always null and the `Item` is always non null but only contains something, if the parsed json is an array.
The problem seems to me that the `CreateFromDiscriminatorValue` function only looks at the discriminator value even if there is none and not at the type of the json that is being parsed.

The solution I found for my usecase looks like this:
```cs
public static Result_OneOrManyItems CreateFromDiscriminatorValue(IParseNode parseNode) {
_ = parseNode ?? throw new ArgumentNullException(nameof(parseNode));
var result = new Result_OneOrManyItems();
var list = parseNode.GetCollectionOfObjectValues(ApiSdk.Models.Item.CreateFromDiscriminatorValue)?.ToList() as List;
if((list.Count ?? 0) == 0) {
result.ResultOneOrManyItemsItem = new ApiSdk.Models.Item();
}
else {
result.Item = list;
}
return result;
}
```
This is far from ideal because it does not allow empty lists and does not use the real underling type. I also have to touch a lot of generated code for this solution which is not convenient and error prone.

This Issue is probably similar to #2338 but it wasn't 100% my use case, hence I created a new issue.
I also saw a similar API description in this issue #3963 but the problem was a different one.

If this is not fixable in the short term is there a better workaround than adjusting all implementations of `CreateFromDiscriminatorValue` by hand?

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.