Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to JsonSerializationWriter.WriteNonParsableObjectValue for anonymous objects. #307

Merged
merged 8 commits into from
Jul 31, 2024
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.9.12] - 2024-07-30

- Fix non IParasable object serialization.
- Add basic support for serializing dictionary values in AdditionalData.

## [1.9.11] - 2024-07-22

- Obsoletes custom decompression handler in favor of native client capabilities.
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<!-- Common default project properties for ALL projects-->
<PropertyGroup>
<VersionPrefix>1.9.11</VersionPrefix>
<VersionPrefix>1.9.12</VersionPrefix>
<VersionSuffix></VersionSuffix>
<!-- This is overidden in test projects by setting to true-->
<IsTestProject>false</IsTestProject>
Expand Down
38 changes: 34 additions & 4 deletions src/serialization/json/JsonSerializationWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// ------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
Expand Down Expand Up @@ -344,7 +345,7 @@ public void WriteCollectionOfObjectValues<T>(string? key, IEnumerable<T>? values
/// <param name="key">The key to be used for the written value. May be null.</param>
/// <param name="values">The enum values to be written.</param>
#if NET5_0_OR_GREATER
public void WriteCollectionOfEnumValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]T>(string? key, IEnumerable<T?>? values) where T : struct, Enum
public void WriteCollectionOfEnumValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>(string? key, IEnumerable<T?>? values) where T : struct, Enum
#else
public void WriteCollectionOfEnumValues<T>(string? key, IEnumerable<T?>? values) where T : struct, Enum
#endif
Expand All @@ -360,6 +361,32 @@ public void WriteCollectionOfEnumValues<T>(string? key, IEnumerable<T?>? values)
}
}
/// <summary>
/// Writes the specified dictionary to the stream with an optional given key.
/// </summary>
/// <param name="key">The key to be used for the written value. May be null.</param>
/// <param name="values">The dictionary of values to be written.</param>
#if NET5_0_OR_GREATER
private void WriteDictionaryValue<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>(string? key, T values) where T : IDictionary
#else
private void WriteDictionaryValue<T>(string? key, T values) where T : IDictionary
#endif
{
if(values != null)
{
if(!string.IsNullOrEmpty(key))
writer.WritePropertyName(key!);

writer.WriteStartObject();
foreach(DictionaryEntry entry in values)
{
if(entry.Key is not string keyStr)
throw new InvalidOperationException($"Error serializing dictionary value with key {key}, only string keyed dictionaries are supported.");
WriteAnyValue(keyStr, entry.Value);
}
writer.WriteEndObject();
}
}
/// <summary>
/// Writes the specified byte array as a base64 string to the stream with an optional given key.
/// </summary>
/// <param name="key">The key to be used for the written value. May be null.</param>
Expand Down Expand Up @@ -433,7 +460,7 @@ public void WriteAdditionalData(IDictionary<string, object> value)
}

#if NET5_0_OR_GREATER
private void WriteNonParsableObjectValue<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string? key,T value)
private void WriteNonParsableObjectValue<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string? key, T value)
#else
private void WriteNonParsableObjectValue<T>(string? key, T value)
#endif
Expand All @@ -444,7 +471,7 @@ private void WriteNonParsableObjectValue<T>(string? key, T value)
if(value == null)
writer.WriteNullValue();
else
foreach(var oProp in typeof(T).GetProperties())
foreach(var oProp in value.GetType().GetProperties())
baywet marked this conversation as resolved.
Show resolved Hide resolved
WriteAnyValue(oProp.Name, oProp.GetValue(value));
writer.WriteEndObject();
}
Expand Down Expand Up @@ -512,14 +539,17 @@ private void WriteAnyValue<T>(string? key, T value)
writer.WritePropertyName(key!);
jsonElement.WriteTo(writer);
break;
case IDictionary dictionary:
WriteDictionaryValue(key, dictionary);
break;
case object o:
WriteNonParsableObjectValue(key, o);
break;
case null:
WriteNullValue(key);
break;
default:
throw new InvalidOperationException($"error serialization additional data value with key {key}, unknown type {value?.GetType()}");
throw new InvalidOperationException($"Error serializing additional data value with key {key}, unknown type {value?.GetType()}");
}
}

Expand Down
27 changes: 26 additions & 1 deletion tests/serialization/json/JsonSerializationWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ public void WritesSampleObjectValue()
{"businessPhones", new List<string>() {"+1 412 555 0109"}}, // write collection of primitives value
{"endDateTime", new DateTime(2023,03,14,0,0,0,DateTimeKind.Utc) }, // ensure the DateTime doesn't crash
{"manager", new TestEntity{Id = "48d31887-5fad-4d73-a9f5-3c356e68a038"}}, // write nested object value
{"anonymousObject", new {Value1 = true, Value2 = "", Value3 = new List<string>{ "Value3.1", "Value3.2"}}}, // write nested object value
{"dictionaryString", new Dictionary<string, string>{{"91bbe8e2-09b2-482b-a90e-00f8d7e81636", "b7992f48-a51b-41a1-ace5-4cebb7f111d0"}, { "ed64c116-2776-4012-94d1-a348b9d241bd", "55e1b4d0-2959-4c71-89b5-385ba5338a1c" }, }}, // write a Dictionary
{"dictionaryTestEntity", new Dictionary<string, TestEntity>{{ "dd476fc9-7e97-4a4e-8d40-6c3de7432eb3", new TestEntity { Id = "dd476fc9-7e97-4a4e-8d40-6c3de7432eb3" } }, { "ffa5c351-7cf5-43df-9b55-e12455cf6eb2", new TestEntity { Id = "ffa5c351-7cf5-43df-9b55-e12455cf6eb2" } }, }}, // write a Dictionary
}
};

using var jsonSerializerWriter = new JsonSerializationWriter();
// Act
jsonSerializerWriter.WriteObjectValue(string.Empty, testEntity);
Expand All @@ -65,7 +69,10 @@ public void WritesSampleObjectValue()
"\"weightInKgs\":51.80," +
"\"businessPhones\":[\"\\u002B1 412 555 0109\"]," +
"\"endDateTime\":\"2023-03-14T00:00:00+00:00\"," +
"\"manager\":{\"id\":\"48d31887-5fad-4d73-a9f5-3c356e68a038\"}" +
"\"manager\":{\"id\":\"48d31887-5fad-4d73-a9f5-3c356e68a038\"}," +
"\"anonymousObject\":{\"Value1\":true,\"Value2\":\"\",\"Value3\":[\"Value3.1\",\"Value3.2\"]}," +
"\"dictionaryString\":{\"91bbe8e2-09b2-482b-a90e-00f8d7e81636\":\"b7992f48-a51b-41a1-ace5-4cebb7f111d0\",\"ed64c116-2776-4012-94d1-a348b9d241bd\":\"55e1b4d0-2959-4c71-89b5-385ba5338a1c\"}," +
"\"dictionaryTestEntity\":{\"dd476fc9-7e97-4a4e-8d40-6c3de7432eb3\":{\"id\":\"dd476fc9-7e97-4a4e-8d40-6c3de7432eb3\"},\"ffa5c351-7cf5-43df-9b55-e12455cf6eb2\":{\"id\":\"ffa5c351-7cf5-43df-9b55-e12455cf6eb2\"}}" +
"}";
Assert.Equal(expectedString, serializedJsonString);
}
Expand Down Expand Up @@ -161,6 +168,24 @@ public void WritesSampleCollectionOfObjectValues()
Assert.Equal(expectedString, serializedJsonString);
}

[Fact]
public void DoesntWriteUnsupportedTypes_NonStringKeyedDictionary()
{
// Arrange
var testEntity = new TestEntity()
{
AdditionalData = new Dictionary<string, object>
{
{"nonStringKeyedDictionary", new Dictionary<int, string>{{ 1, "one" }, { 2, "two" }}}
}
};

using var jsonSerializerWriter = new JsonSerializationWriter();
// Act & Assert
var exception = Assert.Throws<InvalidOperationException>(() => jsonSerializerWriter.WriteObjectValue(string.Empty, testEntity));
Assert.Equal("Error serializing dictionary value with key nonStringKeyedDictionary, only string keyed dictionaries are supported.", exception.Message);
}

[Fact]
public void WritesEnumValuesAsCamelCasedIfNotEscaped()
{
Expand Down
Loading