Repository: asyncapi/modelina
Pull request #2297 (2025-06-18 01:16:52)

fix: convert dynamic to correct type before switch check for CSharp
## Description
When deserializing enums from dynamic, the dynamic value is a JsonElemet and should be a different type (string, int, bool etc).
I saw the following errors when trying to deserialize:
```
 Error Message:
         Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : Cannot convert null to 'com.mycompany.app.json_serializer.HousingType' because it is a non-nullable value type
```

We need to check the type of the dynamic before proceeding to the switch case or else we will always see null.

I added a test case that fails without the new changes and passes with the new changes.

## Related Issue
<!-- If this pull request is related to any existing issue, mention it here. -->

## Checklist
- [x] The code follows the project's coding standards and is properly linted (`npm run lint`).
- [x] Tests have been added or updated to cover the changes.
- [ ] Documentation has been updated to reflect the changes.
- [x] All tests pass successfully locally.(`npm run test`).

## Additional Notes
<!-- Add any additional information or context that might be relevant to reviewers. -->


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297)


coveralls: ## Pull Request Test Coverage Report for [Build 15739530956](https://coveralls.io/builds/74222065)


### Details

* **7** of **7**   **(100.0%)**  changed or added relevant lines in **1** file are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.006%**) to **92.556%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/74222065/badge)](https://coveralls.io/builds/74222065) |
| :-- | --: |
| Change from base [Build 15629265044](https://coveralls.io/builds/74135329): |  0.006% |
| Covered Lines: | 6596 |
| Relevant Lines: | 6990 |

---
##### 💛  - [Coveralls](https://coveralls.io)

jonaslagoni:  @rmaced0 can you update your branch with master? I cant seem to do it 🤨 
sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=2297&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=2297&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=2297)



Patch:
diff --git a/src/generators/csharp/presets/JsonSerializerPreset.ts b/src/generators/csharp/presets/JsonSerializerPreset.ts
index c0960166..c23c3d68 100644
--- a/src/generators/csharp/presets/JsonSerializerPreset.ts
+++ b/src/generators/csharp/presets/JsonSerializerPreset.ts
@@ -205,6 +205,37 @@ ${renderer.indent(deserializeProperties, 4)}
 }`;
 }
 
+function renderDynamicValueConverter({ content }: { content: string }): string {
+  const index = content.indexOf('switch (value)');
+  let newContent = content.slice(0, index);
+  newContent = `${newContent}if (value is JsonElement jsonElement)
+    {
+      if (jsonElement.ValueKind == JsonValueKind.String)
+      {
+        value = jsonElement.GetString();
+      }
+      else if (jsonElement.ValueKind == JsonValueKind.Number)
+      {
+        value = jsonElement.GetInt32();
+      }
+      else if (jsonElement.ValueKind == JsonValueKind.False || jsonElement.ValueKind == JsonValueKind.True)
+      {
+        value = jsonElement.GetBoolean();
+      }
+      else if (jsonElement.ValueKind == JsonValueKind.Object)
+      {
+        value = jsonElement.ToString();
+      }
+      else
+      {
+        return null;
+      }
+    }\n\n    `;
+  newContent += content.slice(index);
+
+  return newContent;
+}
+
 /**
  * Preset which adds `Serialize` and `Deserialize` functions to the class.
  *
@@ -252,5 +283,11 @@ ${renderer.indent(serialize)}
 }
 `;
     }
+  },
+  enum: {
+    self({ renderer, content }) {
+      renderer.dependencyManager.addDependency('using System.Text.Json;');
+      return renderDynamicValueConverter({ content });
+    }
   }
 };


Test patch:
diff --git a/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap b/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap
index 64df1052..24752d3f 100644
--- a/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap
+++ b/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap
@@ -205,6 +205,30 @@ public static class EnumTestExtensions
 
   public static EnumTest? ToEnumTest(dynamic? value)
   {
+    if (value is JsonElement jsonElement)
+    {
+      if (jsonElement.ValueKind == JsonValueKind.String)
+      {
+        value = jsonElement.GetString();
+      }
+      else if (jsonElement.ValueKind == JsonValueKind.Number)
+      {
+        value = jsonElement.GetInt32();
+      }
+      else if (jsonElement.ValueKind == JsonValueKind.False || jsonElement.ValueKind == JsonValueKind.True)
+      {
+        value = jsonElement.GetBoolean();
+      }
+      else if (jsonElement.ValueKind == JsonValueKind.Object)
+      {
+        value = jsonElement.ToString();
+      }
+      else
+      {
+        return null;
+      }
+    }
+
     switch (value)
     {
       case \\"Some enum String\\": return EnumTest.SOME_SPACE_ENUM_SPACE_STRING;
diff --git a/test/runtime/runtime-csharp/runtime-csharp/test/models/json_serializer/Address.cs b/test/runtime/runtime-csharp/runtime-csharp/test/models/json_serializer/Address.cs
index de0f6ab5..55efc210 100644
--- a/test/runtime/runtime-csharp/runtime-csharp/test/models/json_serializer/Address.cs
+++ b/test/runtime/runtime-csharp/runtime-csharp/test/models/json_serializer/Address.cs
@@ -32,5 +32,28 @@ public class AddressTests
         string expectedJsonString = "{\"house_number\":1,\"marriage\":true,\"members\":2,\"array_type\":[1,\"test\"],\"nestedObject\":{\"test\":\"test\"},\"enumTest\":\"test\",\"houseType\":\"flat\",\"roofType\":\"straw\",\"test_not_used\":2}";
         Assert.That(actualJsonString, Is.EqualTo(expectedJsonString));
     }
-}
 
+    [Test]
+    public void TestDeserializingFullModel()
+    {
+        string jsonString = "{\"house_number\":1,\"marriage\":true,\"members\":2,\"array_type\":[1,\"test\"],\"nestedObject\":{\"test\":\"test\"},\"enumTest\":\"test\",\"houseType\":\"flat\",\"roofType\":\"straw\"}";
+
+        Address address = Address.Deserialize(jsonString);
+
+        Assert.That(address.HouseNumber, Is.EqualTo(1));
+        Assert.That(address.Marriage, Is.EqualTo(true));
+        Assert.That(address.Members, Is.Not.Null);
+        address.Members.TryGetInt32(out int membersValue);
+        Assert.That(membersValue, Is.EqualTo(2));
+        Assert.That(address.ArrayType.Length, Is.EqualTo(2));
+        address.ArrayType[0].TryGetInt32(out int arrayTypeValue0);
+        Assert.That(arrayTypeValue0, Is.EqualTo(1));
+        Assert.That(address.ArrayType[1].GetString(), Is.EqualTo("test"));
+        Assert.That(address.NestedObject, Is.Not.Null);
+        Assert.That(address.NestedObject.Test, Is.EqualTo("test"));
+        Assert.That(address.EnumTest, Is.EqualTo(EnumTest.TEST));
+        Assert.That(address.HouseType, Is.EqualTo(HousingType.FLAT));
+        Assert.That(address.RoofType, Is.EqualTo(TypeOfRoof.STRAW));
+        Assert.That(address.AdditionalProperties, Is.Null);
+    }
+}