JoshClose / CsvHelper

Library to help reading and writing CSV files
http://joshclose.github.io/CsvHelper/
Other
4.73k stars 1.06k forks source link

FastDynamicObject broke serialization #2251

Closed davesmits closed 5 months ago

davesmits commented 5 months ago

Describe the bug in !https://github.com/JoshClose/CsvHelper/commit/d3852d37ad33df04b069882c6a276d27a706f8ed the FastDynamicObject was introduced. Before that the ExpandoObject was used.

The Mongo driver was able to serialize ExpandoObject. The FastDynamicObject isn't able to serialize to bson

To Reproduce

var csvConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture) { Delimiter = delimiter };
        using var csv = new CsvReader(reader, csvConfiguration);

        var result = csv.GetRecords<object>()
            .Select(x => x.ToBsonDocument())
            .ExtendWithDatatypes(configurations)
            .ToList();

First it breaks because the FastDynamicObject isnt allowed by default. That is easy to fix by adding it to the allowed types

        var objectSerializer = new ObjectSerializer(type => ObjectSerializer.DefaultAllowedTypes(type) || type.Name == "FastDynamicObject");
        BsonSerializer.TryRegisterSerializer(objectSerializer);

Then it breaks with this stacktrace

Message: 
  Test method Pandora.AdhocDataSources.Mongo.Tests.Services.MongoFileHandlerTests.MongoFileHandler_HandleFileAsync_CSV threw exception: 
  System.NotSupportedException: Specified method is not supported.

Stack Trace: 
  Object>>.GetEnumerator()
  DictionarySerializerBase`3.SerializeDocumentRepresentation(BsonSerializationContext context, TDictionary value)
  DictionarySerializerBase`3.SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, TDictionary value)
  ClassSerializerBase`1.Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value)
  IBsonSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, Object value)
  IBsonSerializerExtensions.Serialize(IBsonSerializer serializer, BsonSerializationContext context, Object value)
  ObjectSerializer.SerializeDiscriminatedValue(BsonSerializationContext context, BsonSerializationArgs args, Object value, Type actualType)
  ObjectSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, Object value)
  IBsonSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, Object value)
  BsonExtensionMethods.ToBsonDocument(Object obj, Type nominalType, IBsonSerializer serializer, Action`1 configurator, BsonSerializationArgs args)
  BsonExtensionMethods.ToBsonDocument[TNominalType](TNominalType obj, IBsonSerializer`1 serializer, Action`1 configurator, BsonSerializationArgs args)
  <>c.<GetDocumentsFromCsvFile>b__11_0(Object x) line 92
  SelectEnumerableIterator`2.ToArray()
  BsonDocumentExtensions.ExtendWithDatatypes(IEnumerable`1 documents, Dictionary`2 configurations) line 16
  MongoFileHandler.GetDocumentsFromCsvFile(IFormFile file, Dictionary`2 configurations) line 91
  MongoFileHandler.HandleFileAsync(AdhocDataSource adhocDataSource, IFormFile file) line 57
  MongoFileHandler.HandleFileAsync(AdhocDataSource adhocDataSource, IFormFile file) line 69
  MongoFileHandlerTests.MongoFileHandler_HandleFileAsync_CSV() line 122

Expected behavior Expect that it keeps working as is and be able to serialize to BSON

DavidLievrouw commented 5 months ago

Not just in the context of MongoDB, imo. I have a similar issue when just enumerating the output of FastDynamicObject.

        using var reader = _fileService.OpenStreamReader(filePath);

        using var csv = new CsvReader(reader, new CsvConfiguration(CultureInfo.InvariantCulture) {
            Delimiter = delimiter,
            IgnoreBlankLines = true,
            HasHeaderRecord = hasHeaders
        });

        var records = (await csv.GetRecordsAsync<dynamic>().ToListAsync())
            .Select(d => d as IDictionary<string, object>);

        foreach (var recordDic in records) {
            // This next line throws
            foreach (var (key, value) in recordDic) {
                // Do something...
            }
        }
System.NotSupportedException
Specified method is not supported.
   at CsvHelper.FastDynamicObject.System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.String,System.Object>>.GetEnumerator()
JoshClose commented 5 months ago

People use things in ways I never expect! I'll implement the rest of IDictionary<string, object>.

JoshClose commented 5 months ago

I have pushed the changes. Can you pull down the source and test if this satisfies your needs?

davesmits commented 5 months ago

@JoshClose wauw that is fast. I will give it a spin; but weekend just started so need to do it somewhere between some other obligations

JoshClose commented 5 months ago

Fixed in 32.0.1 on NuGet. If there are more issues, open this back up and I'll fix them.

davesmits commented 5 months ago

Works for me! thanks

DavidLievrouw commented 5 months ago

Yep, thank you.