diff --git a/src/Core/Services/MetadataProviders/Converters/DatabaseObjectConverter.cs b/src/Core/Services/MetadataProviders/Converters/DatabaseObjectConverter.cs index d1894c7c45..6d625c7f9d 100644 --- a/src/Core/Services/MetadataProviders/Converters/DatabaseObjectConverter.cs +++ b/src/Core/Services/MetadataProviders/Converters/DatabaseObjectConverter.cs @@ -34,7 +34,7 @@ public override DatabaseObject Read(ref Utf8JsonReader reader, Type typeToConver DatabaseObject objA = (DatabaseObject)JsonSerializer.Deserialize(document, concreteType, options)!; - foreach (PropertyInfo prop in objA.GetType().GetProperties().Where(IsSourceDefinitionProperty)) + foreach (PropertyInfo prop in objA.GetType().GetProperties().Where(IsSourceDefinitionOrDerivedClassProperty)) { SourceDefinition? sourceDef = (SourceDefinition?)prop.GetValue(objA); if (sourceDef is not null) @@ -73,25 +73,23 @@ public override void Write(Utf8JsonWriter writer, DatabaseObject value, JsonSeri writer.WritePropertyName(prop.Name); object? propVal = prop.GetValue(value); - Type propType = prop.PropertyType; - // Only escape columns for properties whose type is exactly SourceDefinition (not subclasses). - // This is because, we do not want unnecessary mutation of subclasses of SourceDefinition unless needed. - if (IsSourceDefinitionProperty(prop) && propVal is SourceDefinition sourceDef && propVal.GetType() == typeof(SourceDefinition)) + // Only escape columns for properties whose type(derived type) is SourceDefinition. + if (IsSourceDefinitionOrDerivedClassProperty(prop) && propVal is SourceDefinition sourceDef) { EscapeDollaredColumns(sourceDef); } - JsonSerializer.Serialize(writer, propVal, propType, options); + JsonSerializer.Serialize(writer, propVal, options); } writer.WriteEndObject(); } - private static bool IsSourceDefinitionProperty(PropertyInfo prop) + private static bool IsSourceDefinitionOrDerivedClassProperty(PropertyInfo prop) { - // Only return true for properties whose type is exactly SourceDefinition (not subclasses) - return prop.PropertyType == typeof(SourceDefinition); + // Return true for properties whose type is SourceDefinition or any class derived from SourceDefinition + return typeof(SourceDefinition).IsAssignableFrom(prop.PropertyType); } /// diff --git a/src/Service.Tests/UnitTests/SerializationDeserializationTests.cs b/src/Service.Tests/UnitTests/SerializationDeserializationTests.cs index a4cb2848ad..12ae3ee993 100644 --- a/src/Service.Tests/UnitTests/SerializationDeserializationTests.cs +++ b/src/Service.Tests/UnitTests/SerializationDeserializationTests.cs @@ -299,8 +299,11 @@ public void TestDictionaryDatabaseObjectSerializationDeserialization_WithDollarC Dictionary dict = new() { { "person", _databaseTable } }; string serializedDict = JsonSerializer.Serialize(dict, _options); - Dictionary deserializedDict = JsonSerializer.Deserialize>(serializedDict, _options)!; + // Assert that the serialized JSON contains the escaped dollar sign in column name + Assert.IsTrue(serializedDict.Contains("DAB_ESCAPE$FirstName"), + "Serialized JSON should contain the dollar-prefixed column name in SourceDefinition's Columns."); + Dictionary deserializedDict = JsonSerializer.Deserialize>(serializedDict, _options)!; DatabaseTable deserializedDatabaseTable = (DatabaseTable)deserializedDict["person"]; Assert.AreEqual(deserializedDatabaseTable.SourceType, _databaseTable.SourceType); @@ -322,14 +325,22 @@ public void TestDatabaseViewSerializationDeserialization_WithDollarColumn() TestTypeNameChanges(_databaseView, "DatabaseView"); + Dictionary dict = new(); + dict.Add("person", _databaseView); + // Test to catch if there is change in number of properties/fields // Note: On Addition of property make sure it is added in following object creation _databaseView and include in serialization // and deserialization test. int fields = typeof(DatabaseView).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Length; Assert.AreEqual(fields, 6); - string serializedDatabaseView = JsonSerializer.Serialize(_databaseView, _options); - DatabaseView deserializedDatabaseView = JsonSerializer.Deserialize(serializedDatabaseView, _options)!; + string serializedDatabaseView = JsonSerializer.Serialize(dict, _options); + // Assert that the serialized JSON contains the escaped dollar sign in column name + Assert.IsTrue(serializedDatabaseView.Contains("DAB_ESCAPE$FirstName"), + "Serialized JSON should contain the dollar-prefixed column name in SourceDefinition's Columns."); + Dictionary deserializedDict = JsonSerializer.Deserialize>(serializedDatabaseView, _options)!; + + DatabaseView deserializedDatabaseView = (DatabaseView)deserializedDict["person"]; Assert.AreEqual(deserializedDatabaseView.SourceType, _databaseView.SourceType); deserializedDatabaseView.Equals(_databaseView); @@ -349,14 +360,21 @@ public void TestDatabaseStoredProcedureSerializationDeserialization_WithDollarCo TestTypeNameChanges(_databaseStoredProcedure, "DatabaseStoredProcedure"); + Dictionary dict = new(); + dict.Add("person", _databaseStoredProcedure); + // Test to catch if there is change in number of properties/fields // Note: On Addition of property make sure it is added in following object creation _databaseStoredProcedure and include in serialization // and deserialization test. int fields = typeof(DatabaseStoredProcedure).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Length; Assert.AreEqual(fields, 6); - string serializedDatabaseSP = JsonSerializer.Serialize(_databaseStoredProcedure, _options); - DatabaseStoredProcedure deserializedDatabaseSP = JsonSerializer.Deserialize(serializedDatabaseSP, _options)!; + string serializedDatabaseSP = JsonSerializer.Serialize(dict, _options); + // Assert that the serialized JSON contains the escaped dollar sign in column name + Assert.IsTrue(serializedDatabaseSP.Contains("DAB_ESCAPE$FirstName"), + "Serialized JSON should contain the dollar-prefixed column name in SourceDefinition's Columns."); + Dictionary deserializedDict = JsonSerializer.Deserialize>(serializedDatabaseSP, _options)!; + DatabaseStoredProcedure deserializedDatabaseSP = (DatabaseStoredProcedure)deserializedDict["person"]; Assert.AreEqual(deserializedDatabaseSP.SourceType, _databaseStoredProcedure.SourceType); deserializedDatabaseSP.Equals(_databaseStoredProcedure);