Chris K
Chris K

Reputation: 560

Polymorphism and serialization with System.Text.Json is creating a duplicate key error in OpenAPI

I am working on a project which internally uses Newtonsoft.Json to serialize and deserialize complex objects including type information. This works as it should. We are using Minimal API for our API, using ASP.NET Core 9, and Swagger.

I understand that as we are using Minimal API we have no choice but to use System.Text.Json, and this is causing some issues with polymorphism, resulting in either no object types in the JSON returned, or when trying to use [JsonDerivedType(...)], an error at run time:

System.ArgumentException: An item with the same key has already been added. Key: [keyname...]

I have a feeling that this is caused by the degree of inheritance. I can't post the actual code here, but the following sums up the issue I am facing (description below) - in the solution, the interfaces and classes are all separate files, but for brevity I've listed them in one block here:

public interface IContainer: IObject {
  List<IObject> Children {get;set;}
}

public interface IObject {
  int Id {get;set;} //Lots of other parameters
}


public class ObjectType1 : DisplayBaseClass, IChild { //class }

public class ObjectType2 : DisplayBaseClass, IContainer { //class }

public class ObjectType3 : SomeOtherClass, IChild, IAnotherInterface, IAndAnother { //class }

IObject is implemented on objects that can be rendered. However, some objects can contain children, and those objects implement IContainer.

The first thing I tried was:

[JsonDerivedType(typeof(ObjectType1), "[type discriminator here...]")]
[JsonDerivedType(typeof(ObjectType2), "[type discriminator here...]")]
[JsonDerivedType(typeof(ObjectType3), "[type discriminator here...]")]
public interface IObject {...

This causes Swagger to fail with a 500 internal error, with the System.ArgumentException referred to above - duplicate key. Specifically, ObjectType2 was the offending item.

I made progress in so far as I separated the [JsonDerivedType] attributes into separate interface definitions:

//in IContainer.cs
[JsonPolymorphic(UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
[JsonDerivedType(typeof(ObjectType2), "[type discriminator here...]")]
public interface IContainer {...


//In IChild.cs
[JsonDerivedType(typeof(ObjectType1), "[type discriminator here...]")]
public interface IChild{...

(Note I have not included ObjectType3 here). This will compile and run and Swagger will display and the API endpoint can be called. Some JSON is returned, but it does not include any type information - this I will deal with another time.

The problem is I can't work out how to add a derived type for ObjectType3 - if I add [JsonDerivedType(nameof(ObjectType3), "...")] to IObject.cs, I end up with duplicate keys again, and can't use Swagger.

Can anyone explain what I'm doing wrong, specifically in regards to how to add the derived types without it creating duplicate keys?

Upvotes: 0

Views: 153

Answers (1)

Chris K
Chris K

Reputation: 560

Problem has been resolved by using Swagger to generate the json file. Also as mentioned in one of the links from @dbc, [JsonDerivedType] needed to be added to all classes and interfaces in the inheritance hierarchy. This failed when using app.MapOpenApi() due to the duplicate key issue.

Upvotes: 0

Related Questions