How can I get OpenApi Generator to convert Dictionary<int, string> correctly?
Asked Answered
H

1

7

Right now my Api returns a model with a Dictionary<int, string> property in it:

public Dictionary<int, string> Subdivisions { get; set; }

When I run OpenApi Generator, the output class has

public Dictionary<string, string> Subdivisions { get; set; }

I know the JSON spec doesn't allow integer keys, but this really screws up consumers of that API which are expecting <int, string>.

What can I do to ensure my output class has Dictionary<int, string> instead of Dictionary<string, string>?

Heiney answered 12/5, 2021 at 1:13 Comment(5)
can you add OpenApi json?Cornelius
@VovaBilyachat not really. It's company data and I can't share that.Heiney
Doubt it possible. You can read through NSwag's discussion about it here and here. It sounds like they may have completed the feature, maybe. This feature is simply not part of the OpenAPI spec (being able to define the type of key, since keys can only ever be strings in the JSON world).Annulation
May I ask how you handle the consumption that you cannot change a string to an integer after you get it?Marianmariana
Consider using an example in the response documentation.Bondstone
K
9

Since Swagger/OpenAPI definitions are specific to REST, they will likely continue to support models that can be expressed in JSON. JSON is a language-agnostic serialization format that supports objects and collections, but it has no methods or actual implementation. REST provides the methods to mutate the server's copy of the JSON, and the consumer is then required to interpret the response as needed. Since there is no Map in JSON, the default for an IDictionary is to serialize it as an object using it's keys as property names--the benefit is the map-like lookup function but the cost is that property names have to be strings.

So, what would a Dictionary<int,string> look like in valid JSON? With a custom serializer and deserializer we could expect a list of Key-Value-Pair objects:

[
    {"key":1, "value": "one"},
    {"key":2, "value": "two"}
]

You would publish the REST operation with this KVP model, then you would put a custom converter on your API to convert this list of KVPs to and from a Dictionary. This would be conformant to OAS/JSON and allow non-REST consumers of that API/service which are expecting <int, string>. There are several examples of how to do this:

C# JSON custom serialization https://www.newtonsoft.com/json/help/html/SerializingCollections.htm

However, if you REALLY need to generate code from a swagger definition that doesn't conform to the standard, it is possible to overwrite the generator to meet your needs. Consider it a warning that your implementation is against-the-grain, and all the effort you put into this codegen may be undone by minor releases. It's better to find a way to go in the direction of the community for the longest viability of your solution.

With that said, the generator is a simple Java program that reads the spec and outputs text files using Mustache templates. The "CodeGen" classes parse the spec into arrays following the language specific logic, then the "Mustache" templates are applied logic-less over the arrays to generate code. By reading the Java as a guide, I am usually able to generate my custom classes by modifying only the Mustache templates or the configuration. Unfortunately for your case, the returnType for an Operation only supports List or primitive, so the CodeGen Java would need to be modified if you want an API generated to return a Dictionary. Be sure to bring a paddle if you go this far up stream!

Here is where the IDictonary type is set for Maps. Here is where it is set generically, and here is for C#. This is the Mustache that creates a model property, and this is the how the API operation is created.

Kvass answered 14/5, 2021 at 15:52 Comment(3)
Very informative and great documentation links!Papistry
Thank you for the comprehensive answer. I'm working on a different ticket at the moment, but I'll be coming back to this in a few days. I'll mark it as correct when I implement the suggestion and add any relevant notes.Heiney
<Pedant>REST architecture makes no mention of JSON at all. ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htmUnprofitable

© 2022 - 2024 — McMap. All rights reserved.