OpenAPITools / OpenAPITools/openapi-generator
[BUG] [C#] Using "disallowAdditionalPropertiesIfNotPresent": false is not equivalent to setting "additionalProperties":true
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator (example)?
- Have you tested with the latest master to confirm the issue still exists?
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
I'm using the csharp-netcore generator and running into issues with additional properties. I have a model with some fixed properties, and you could add additional dynamic properties. When in the spec I set additionalProperties: true for that specific model, the generated class extends from a dictionary which causes issues with json serialization. However, when I remove additionalProperties: true from the spec and use "disallowAdditionalPropertiesIfNotPresent": false the generated model looks as I expected; The generated class doesn't extend from a dictionary, and an AdditionalProperties property is generated. Why disallowAdditionalPropertiesIfNotPresent:false is not equivalent to explicitly set additionalProperties:true?
Using "disallowAdditionalPropertiesIfNotPresent": false
Spec YAML:
UserProfile:
type: object
properties:
city:
type: string
maxLength: 128
nullable: true
...
Debug Model metadata
"importPath" : "Model.UserProfile",
"model" : {
"anyOf" : [ ],
"oneOf" : [ ],
"allOf" : [ ],
"name" : "UserProfile",
"classname" : "UserProfile",
"classVarName" : "UserProfile",
"modelJson" : "{\r\n \"type\" : \"object\",\r\n \"properties\" : {\r\n \"city\" : {\r\n \"maxLength\" : 128,\r\n \"type\" : \"string\",\r\n \"nullable\" : true\r\n } }\r\n}",
"dataType" : "Object", !!! NOTE this
"classFilename" : "UserProfile",
"isAlias" : false,
"isString" : false,
"isInteger" : false,
"isLong" : false,
"isNumber" : false,
"isNumeric" : false,
"isFloat" : false,
"isDouble" : false,
"isDate" : false,
"isDateTime" : false,
"isDecimal" : false,
"isShort" : false,
"isUnboundedInteger" : false,
"isPrimitiveType" : false,
"isBoolean" : false,
"additionalPropertiesIsAnyType" : true, !!! NOTE this
Generated class
/// <summary>
/// Template: ModelGeneric
/// UserProfile
/// </summary>
[DataContract(Name = "UserProfile")]
public partial class UserProfile : IEquatable<UserProfile>
{
/// <summary>
/// Gets or Sets City
/// </summary>
[DataMember(Name = "city", EmitDefaultValue = true)]
public string City { get; set; }
/// <summary>
/// Gets or Sets additional properties
/// </summary>
[JsonExtensionData]
public IDictionary<string, object> AdditionalProperties { get; set; }
Using additionalProperties: true
spec YAML
UserProfile:
type: object
additionalProperties: true
properties:
city:
type: string
maxLength: 128
nullable: true
Debug Model Metadata
"model" : {
"parent" : "Dictionary<String, Object>", !!! NOTE this
"anyOf" : [ ],
"oneOf" : [ ],
"allOf" : [ ],
"name" : "UserProfile",
"classname" : "UserProfile",
"classVarName" : "UserProfile",
"modelJson" : "{\r\n \"type\" : \"object\",\r\n \"properties\" : {\r\n \"city\" : {\r\n \"maxLength\" : 128,\r\n \"type\" : \"string\",\r\n \"nullable\" : true\r\n },\r\n \"additionalProperties\" : true\r\n}",
"dataType" : "Dictionary", !!! NOTE this
"classFilename" : "UserProfile",
"isAlias" : false,
"isString" : false,
"isInteger" : false,
"isLong" : false,
"isNumber" : false,
"isNumeric" : false,
"isFloat" : false,
"isDouble" : false,
"isDate" : false,
"isDateTime" : false,
"isDecimal" : false,
"isShort" : false,
"isUnboundedInteger" : false,
"isPrimitiveType" : false,
"isBoolean" : false,
"additionalPropertiesIsAnyType" : true, !!! NOTE this
Generated class
/// <summary>
/// Template: ModelGeneric
/// UserProfile
/// </summary>
[DataContract(Name = "UserProfile")]
public partial class UserProfile : Dictionary<String, Object>, IEquatable<UserProfile> /// It now extends from Dictionary
{
/// <summary>
/// Gets or Sets City
/// </summary>
[DataMember(Name = "city", EmitDefaultValue = true)]
public string City { get; set; }
/// <summary>
/// Gets or Sets additional properties
/// </summary>
[JsonExtensionData]
public IDictionary<string, object> AdditionalProperties { get; set; }
Is there any reason I don't get the same output for equivalent configuration?
openapi-generator version
6.0.1
OpenAPI declaration file content or URL
PRovided above
Generation Details
openapi-generator-cli generate -g csharp-netcore --skip-validate-spec
openapi-generator-cli version
6.0.0-beta
Steps to reproduce
Run >openapi-generator-cli generate -g csharp-netcore --skip-validate-spec
Related issues/PRs
https://github.com/OpenAPITools/openapi-generator/issues/9798
https://github.com/OpenAPITools/openapi-generator/issues/9727
https://github.com/OpenAPITools/openapi-generator/pull/7396
Suggest a fix
I would expect equivalent output for equivalent spec configuration. If I have a model with fixed properties AND additionalProperties:true the generated model should not extend from Dictionary since it causes serialization issues. Note that using "disallowAdditionalPropertiesIfNotPresent": false only and not additionalProperties:true behaves as expected.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the csharp-netcore generator using the reported openapi-generator-cli command and the UserProfile schemas shown in the issue. Compare the generated metadata and C# models for omitted versus explicit additionalProperties: true; done means equivalent configurations produce the expected non-dictionary model with fixed properties and additional properties support.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, openapi
- Domain
- backend-api-design, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100