NSwag
NSwag copied to clipboard
Optional property marked as required
I'll repost the entire question posted on Stackoverflow as it looks like a NSwag issue. Did I misconfigure something I've missed or is this an NSwag bug?
I'm using NSagStudio to generate a c# client for an OpenAPI 3.0.1 service. Since the latest update I get an issue where a property marked as required is missing from the retrieved data and thus cannot be deserialized.
I'm trying to understand if
- The OpenAPI spec of the service is incomplete/wrong
- I've misconfigured something with NSwagStudio
- There is a bug in NSwagStudio
OpenAPI 3.0.1 service spec: https://raw.githubusercontent.com/admin-ch/CovidCertificate-Apidoc/main/open-api/api-doc.yaml
NSwagStudio configuration:
{
"runtime": "NetCore31",
"defaultVariables": null,
"documentGenerator": {
"fromDocument": {
"json": "[see URL]",
"url": "https://raw.githubusercontent.com/admin-ch/CovidCertificate-Apidoc/main/open-api/api-doc.yaml",
"output": null,
"newLineBehavior": "Auto"
}
},
"codeGenerators": {
"openApiToCSharpClient": {
"clientBaseClass": "BagCovidCertificateClientInternalBase",
"configurationClass": null,
"generateClientClasses": true,
"generateClientInterfaces": false,
"clientBaseInterface": null,
"injectHttpClient": false,
"disposeHttpClient": true,
"protectedMethods": [],
"generateExceptionClasses": true,
"exceptionClass": "BagCovidCertificateApiException",
"wrapDtoExceptions": true,
"useHttpClientCreationMethod": false,
"httpClientType": "System.Net.Http.HttpClient",
"useHttpRequestMessageCreationMethod": true,
"useBaseUrl": true,
"generateBaseUrlProperty": true,
"generateSyncMethods": false,
"generatePrepareRequestAndProcessResponseAsAsyncMethods": false,
"exposeJsonSerializerSettings": false,
"clientClassAccessModifier": "internal",
"typeAccessModifier": "internal",
"generateContractsOutput": false,
"contractsNamespace": null,
"contractsOutputFilePath": null,
"parameterDateTimeFormat": "s",
"parameterDateFormat": "yyyy-MM-dd",
"generateUpdateJsonSerializerSettingsMethod": true,
"useRequestAndResponseSerializationSettings": false,
"serializeTypeInformation": false,
"queryNullValue": "",
"className": "BagCovidCertificateClientInternal",
"operationGenerationMode": "SingleClientFromOperationId",
"additionalNamespaceUsages": [],
"additionalContractNamespaceUsages": [],
"generateOptionalParameters": false,
"generateJsonMethods": false,
"enforceFlagEnums": false,
"parameterArrayType": "System.Collections.Generic.IEnumerable",
"parameterDictionaryType": "System.Collections.Generic.IDictionary",
"responseArrayType": "System.Collections.Generic.ICollection",
"responseDictionaryType": "System.Collections.Generic.IDictionary",
"wrapResponses": false,
"wrapResponseMethods": [],
"generateResponseClasses": true,
"responseClass": "SwaggerResponse",
"namespace": "Documedis.Clients.External.Clients.Internal",
"requiredPropertiesMustBeDefined": true,
"dateType": "System.DateTimeOffset",
"jsonConverters": null,
"anyType": "object",
"dateTimeType": "System.DateTimeOffset",
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.Generic.ICollection",
"arrayInstanceType": "System.Collections.ObjectModel.Collection",
"dictionaryType": "System.Collections.Generic.IDictionary",
"dictionaryInstanceType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.Collection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
"classStyle": "Poco",
"jsonLibrary": "NewtonsoftJson",
"generateDefaultValues": true,
"generateDataAnnotations": true,
"excludedTypeNames": [],
"excludedParameterNames": [],
"handleReferences": false,
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"jsonSerializerSettingsTransformationMethod": null,
"inlineNamedArrays": false,
"inlineNamedDictionaries": false,
"inlineNamedTuples": true,
"inlineNamedAny": false,
"generateDtoTypes": true,
"generateOptionalPropertiesAsNullable": false,
"generateNullableReferenceTypes": false,
"templateDirectory": null,
"typeNameGeneratorType": null,
"propertyNameGeneratorType": null,
"enumNameGeneratorType": null,
"serviceHost": null,
"serviceSchemes": null,
"output": "BagCovidCertificateClientInternal.Generated.cs",
"newLineBehavior": "Auto"
}
}
}
My issue occurs with this element:
IssuableRapidTestDto:
type: object
properties:
code:
type: string
description: Code of rapid test as string.
example: "1232"
display:
type: string
description: Manufacturer and display name of rapid test as string.
example: "Abbott Rapid Diagnostics, Panbio Covid-19 Ag Rapid Test"
validUntil:
type: string
description: Deadline after which the rapid-test can no longer be used to
establish a certificate.
format: date-time
example: 2022-01-06T00:00:00+01:00
Which results in following class being generated:
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.15.5.0 (NJsonSchema v10.6.6.0 (Newtonsoft.Json v12.0.0.0))")]
internal partial class IssuableRapidTestDto
{
[...]
/// <summary>
/// Deadline after which the rapid-test can no longer be used to establish a certificate.
/// </summary>
[Newtonsoft.Json.JsonProperty("validUntil", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public System.DateTimeOffset ValidUntil { get; set; }
[...]
}
My issue occurs because not all IssuableRapidTestDto
have a ValidUntil
date set, when retrieving the data.
If I remove Required = Newtonsoft.Json.Required.DisallowNull
the deserialization works and ValidUntil
holds DateTimeOffet.MinValue
. As this is generated code I don't want to adapt it manually.
I'm having a hard time figuring out if ValidUntil
is actually required or not.
By setting "generateOptionalPropertiesAsNullable": true
(instead of false
before) the property becomes nullable (that's good). But I've noticed ALL properties which are not Newtonsoft.Json.Required.Always
are marked with Newtonsoft.Json.Required.DisallowNull
whereas I'd expect them to be Newtonsoft.Json.Required.Default
(as, from my understanding, they are optional).
I'm still not sure if I misunderstood something or if this is an NSwag bug.
This is a serious issue, has anyone looked at it?
I've encountered this issue again when integrating a new API. The fix I've applied is pretty simple: Replace Required = Newtonsoft.Json.Required.Always
with Required = Newtonsoft.Json.Required.Default
but it is a pain to think of having to do this after every re-regenration of the client.
Same issue but with an api significant in size. Changing all of these by hand is not practical. At this point I may have to separate the work arounds into its own project. This whole thing desperately needs more dedicated maintainers.
Might be able to utilize the ClientBaseClass property of CSharpClientGeneratorSettings to handle this issue instead of manually editing the generated classes.
public class GeneratedClientBase
{
public void UpdateJsonSerializerSettings(JsonSerializerSettings settings)
{
settings.ContractResolver = new SafeContractResolver();
}
}
public class SafeContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var jsonProperty = base.CreateProperty(member, memberSerialization);
jsonProperty.Required = Required.Default;
return jsonProperty;
}
}
Is there any update? Unfortunately it makes NSwag not useable anymore... or is there a propper workaround? -> (To manipulate the specs before is not an option)
@Wangor No update I'm aware of unfortunately
I'm finding this same issue. I guess there hasn't been a workaround?
The workaround of @seansanchez did not work for me, but I managed to get it working using this code:
public partial class GeneratedClient // Replace by your client name
{
partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings)
{
settings.ContractResolver = new SafeContractResolver();
}
}
public class SafeContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var jsonProperty = base.CreateProperty(member, memberSerialization);
jsonProperty.Required = Required.Default;
return jsonProperty;
}
}
However, I strongly feel that https://github.com/RicoSuter/NJsonSchema/pull/1563 would be the definitive fix for this issue, removing the need for this partial class.
+1, hitting this issue for production clients and handling this issue in generated clients everytime is painful. Any update or ETA for fix?