JoshClose / CsvHelper

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

class map for just base types #1297

Open spooky opened 5 years ago

spooky commented 5 years ago

The use case is described by the following snippet:

class Foo
{
    public string X { get; set; }
    public string Y { get; set; }
}

class FooMap: ClassMap<Foo>
{
    public FooMap()
    {
        AutoMap();
        Map(x => x.X).Ignore();
    }
}

class Bar : Foo
{
    public string Frob { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var x = new Bar
        {
            X = "x",
            Y = "y",
            Frob = "frob"
        };

        using (var csv = new CsvWriter(Console.Out))
        {
            csv.Configuration.RegisterClassMap<FooMap>();
            csv.WriteRecords(new[] { x });
        }
    }
}

Current output:

Y
y

Expected output:

Y,Frob
y,frob

I have a bunch of types that I'd like to serialize. These all share a common base class out of which, I'd like to ignore certain properties. Currently, the way that this needs to be done (unless I'm missing something?!?) is that I need to register a class map for each of these. I can create a generic one and that will work, but I still need to register it as many times as I have types that will be serialized - that can grow quickly.

So the feature that would be nice to have is that when a class map is registered for a base class, the derived class auto maps it's properties (just like it is now if you don't register a map) and the base class properties are mapped according to the registration.

AltruCoder commented 5 years ago

I think I have close to what you were looking for. I was having a little trouble getting the result in the same order as your expected output. I don't like having to put the IndexAttribute on Frob. My thinking was setting Y to index 0 should push everything else to the right of it.

class Foo
{
    public string X { get; set; }
    public string Y { get; set; }
}

class Bar : Foo
{
    [Index(1)]
    public string Frob { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var x = new Bar
        {
            X = "x",
            Y = "y",
            Frob = "frob"
        };

        WriteFooBase(new[] { x });
    }

  static void WriteFooBase<T>(IEnumerable<T> items) where T : Foo 
  {
      using (var csv = new CsvWriter(Console.Out))
      {
          var fooMap = new DefaultClassMap<T>();

          fooMap.AutoMap();
          fooMap.Map(f => f.X).Ignore();
          fooMap.Map(f => f.Y).Index(0);

          csv.Configuration.RegisterClassMap(fooMap);
          csv.WriteRecords(items);
      }
   }
}
jzabroski commented 5 years ago

Why can't you just create a generic method that generates a closed type, and register that? You would still need to somehow define the set of closed types to close over.

Similar to what AutoMapper and StructureMap do.

jzabroski commented 5 years ago

Potentially helpful for you: https://github.com/dotnet/corefx/issues/31798