2

Firstly, please forgive any rookie mistakes here - I'm not a regular poster I'm afraid.

Now on to the nitty gritty...

I am trying to use ServiceStack.Text to serialize objects to CSV. If I keep it simple, everything works as expected when serializing objects of a known type.

However I want to serialize many objects and I don't know the type at runtime so I am writing a reusable component where all data is treated as a System.Object. We already do this same routine for Json serialization without problems. But CsvSerializer appears to handle objects differently during serialization.

Sample code

    public void TestIEnumerableObjectSerialization()
    {
        var data = GenerateSampleData();

        JsConfig<DateTime>.SerializeFn = 
            time => new DateTime(time.Ticks, DateTimeKind.Utc).ToString("yyyy-MM-dd HH:mm:ss");

        var csv = CsvSerializer.SerializeToCsv(data);
        Console.WriteLine(csv);

        Assert.Equal("DateTime\r\n"
            + "2017-06-14 00:00:00\r\n"
            + "2017-01-31 01:23:45\r\n",
            csv);
    }

    object[] GenerateSampleData()
    {
        return new object[] {
            new POCO
            {
                DateTime = new DateTime(2017,6,14)
            },
            new POCO
            {
                DateTime = new DateTime(2017,1,31, 01, 23, 45)
            }
         };
    }

    public class POCO
    {
        public DateTime DateTime { get; set; }
    }

The result of this code is that the custom serialization function is not invoked, and the DateTime is written out using the standard ToString() method.

The cause?

The CsvWriter.Write method is inspecting the type of the records and if the type is Object it is treated as a Dictionary<string, object> and CsvDictionaryWriter generates the output.

In turn, CsvDictionaryWriter uses the ToCsvField() extension method to write each property a record.

The problem is that ToCsvField() converts the value of each property to a string using ToString() meaning no custom serialization is performed.

JsonSerializer uses TypeSerializer.SerializeToString(text) to serialize the properties of an Object using any configured custom serialization functions; but this doesn't happen with CsvSerializer.

A possible solution?

Without complicating CsvSerializer, the ToCsvField() extension method could be updated to use TypeSerializer to handle the serialization to a string. Here is what I've been testing with so far:

    public static object ToCsvField(this object text)
    {
        var textSerialized = TypeSerializer.SerializeToString(text).StripQuotes();
        return textSerialized == null || !CsvWriter.HasAnyEscapeChars(textSerialized)
            ? textSerialized
            : string.Concat
              (
                  CsvConfig.ItemDelimiterString,
                  textSerialized.Replace(CsvConfig.ItemDelimiterString, CsvConfig.EscapedItemDelimiterString),
                  CsvConfig.ItemDelimiterString
              );
    }

So far I haven't come across an issue with this change, although someone may prefer not to allocate a new intermediate variable before the return statement.

Hopefully that is enough information, so on to my questions...

  1. Has anyone else experienced this issue?

  2. Am I doing something wrong and should I be serializing Objects a different way?

  3. If this is a suitable fix/implementation of TypeSerializer, what are the chances of this being addressed in an update to ServiceStack.Text? I would raise an issue on GitHub but the ServiceStack.Text repo doesn't let me raise issues.

Thanks in advance.

Andy Doyle
  • 21
  • 3
  • @mythz... Do you know how to run the NUnit tests for ServiceStack.Text? NUnit 3 Test Adapter extension in VS2017 fails to load the tests - possibly due to the new project format? Only test that appears in Test Explorer is a single test from ServiceStack.Text.Tests.xUnit. – Andy Doyle Jun 19 '17 at 08:24
  • You can use the R# NUnit Test runner, they have a [30 day free trial](https://www.jetbrains.com/resharper/download/). You can also run tests using the [NUnit Console Test Runner](https://www.nunit.org/index.php?p=download) on **ServiceStack.Text.Tests.dll** – mythz Jun 19 '17 at 08:28
  • Cheers, merged this change is available from v4.5.13+ that's now [available on MyGet](http://docs.servicestack.net/myget) – mythz Jun 19 '17 at 11:34

0 Answers0