Hi!
Thanks for a great library.
I use it mainly for dumping dataclasses to be used in Matlab. The thing is that they usually using nested classes.
Ex::
public class XYZData
{
public int X;
public int Y;
public int Z;
}
public class SensorsData
{
public XYZData Gyro;
public XYZData Magnometer;
}
To serialize a list of SensorData in this example would give a file:
X,Y,Z,X,Y,Z
...
...
...
As you can see it quite difficult to see what kind of data I send when I send it to someone that doesnt have a clue about the C# classes.
What I expected was:
Gyro.X, Gyro.Y, Gyro.Z, Magnometer.X, Magnometer.Y, Magnometer.Z
I can't find any options about it. Is it implemented?
Use a class map to do it. http://joshclose.github.io/CsvHelper/#mapping-fluent-class-mapping
``` c#
void Main()
{
using( var stream = new MemoryStream() )
using( var writer = new StreamWriter( stream ) )
using( var reader = new StreamReader( stream ) )
using( var csvWriter = new CsvWriter( writer ) )
{
var list = new List
{
new SensorData
{
Gyro = new XYZData { X = 1, Y = 2, Z = 3, },
Magnometer = new XYZData { X = 4, Y = 5, Z = 6, },
},
};
csvWriter.Configuration.RegisterClassMap<SensorDataMap>();
csvWriter.WriteRecords( list );
writer.Flush();
stream.Position = 0;
reader.ReadToEnd().Dump();
}
}
public class XYZData
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}
public class SensorData
{
public XYZData Gyro { get; set; }
public XYZData Magnometer { get; set; }
}
public class SensorDataMap : CsvClassMap
{
public SensorDataMap()
{
References
References
}
}
public class GyroMap : CsvClassMap
{
public GyroMap()
{
Map( m => m.X ).Name( "Gyro.X" );
Map( m => m.Y ).Name( "Gyro.Y" );
Map( m => m.Z ).Name( "Gyro.Z" );
}
}
public class MagnometerMap : CsvClassMap
{
public MagnometerMap()
{
Map( m => m.X ).Name( "Magnometer.X" );
Map( m => m.Y ).Name( "Magnometer.Y" );
Map( m => m.Z ).Name( "Magnometer.Z" );
}
}
Here is the output:
Gyro.X,Gyro.Y,Gyro.Z,Magnometer.X,Magnometer.Y,Magnometer.Z
1,2,3,4,5,6
```
Big thanks for taking time and answering.
I read the documentation and saw the classmap option.
However, that is not an option for me.
I have 10:s, maybe 100 different dataclasses so starting to write classmap for every class was something I wanted to avoid. I was hoping that nested header names was something that could be solved automatically.
(I using a generic "DumpData" function that I more or less send any data object. It then checks the filename and generate either a csv or a xml file depending on given filename. Xml of course give a good nested behaviour by its nature, but its way harder to fast import in programs like MatLab)
I can add that using classmaps also requires that you know that class type in compile time. Something I don't always do (think plugins). I really think nested headers should be supported as an automap option.
For now, you could build it up dynamically. I don't think it would be too much work to add in the ability for nested headers. I will add a task for that.
Here is how you could create a map dynamically.
``` c#
public CsvClassMap CreateClassMap( string prefix, Type type )
{
var mapType = typeof( DefaultCsvClassMap<> ).MakeGenericType( type );
var classMap = (CsvClassMap)Activator.CreateInstance( mapType );
foreach( var property in type.GetProperties() )
{
var propertyMap = new CsvPropertyMap( property );
propertyMap.Name( string.Format( "{0}.{1}", prefix, property.Name ) );
classMap.PropertyMaps.Add( propertyMap );
}
return classMap;
}
```
You would also need to create a maps and add ReferenceMaps to it that contain those types. So basically you'll be doing a lot of the work yourself. I will see if I can look into that feature soon here. I've been very busy lately though.
Here is the task I added for this. https://github.com/JoshClose/CsvHelper/issues/245
Thanks, didn't think you could build them dynamically that way!
Will see if I make I try for it. It's not a very important feature for me since I have the xml dump alternative but one of my colleagues asked for it so I wanted to see if it could be implemented easy.
I thought that it at least could be a nice feature for this nice library.
Yeah, it makes total sense. I just haven't ran into it myself yet, so I didn't think about it.
I just added this functionality. 5954bd270f1cf9b77241ac20e8cf6a260138b3bf
Hi, sorry to bump this issue, but my question is similar.
I'm trying to read from a csv file that has two header rows, where the first header row is a grouping.
In your example that would be:
Gyro,,,Magnometer,,
X,Y,Z,X,Y,Z
1,2,3,4,5,6
Is there a way to parse such a csv file?
Please create a new issue and reference this one. I don't look through closed issues at a later date and will probably miss this.
Most helpful comment
Use a class map to do it. http://joshclose.github.io/CsvHelper/#mapping-fluent-class-mapping
``` c#
void Main()
{
using( var stream = new MemoryStream() )
using( var writer = new StreamWriter( stream ) )
using( var reader = new StreamReader( stream ) )
using( var csvWriter = new CsvWriter( writer ) )
{
var list = new List
{
new SensorData
{
Gyro = new XYZData { X = 1, Y = 2, Z = 3, },
Magnometer = new XYZData { X = 4, Y = 5, Z = 6, },
},
};
}
public class XYZData
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}
public class SensorData
{
public XYZData Gyro { get; set; }
public XYZData Magnometer { get; set; }
}
public class SensorDataMap : CsvClassMap( m => m.Gyro );( m => m.Magnometer );
{
public SensorDataMap()
{
References
References
}
}
public class GyroMap : CsvClassMap
{
public GyroMap()
{
Map( m => m.X ).Name( "Gyro.X" );
Map( m => m.Y ).Name( "Gyro.Y" );
Map( m => m.Z ).Name( "Gyro.Z" );
}
}
public class MagnometerMap : CsvClassMap
{
public MagnometerMap()
{
Map( m => m.X ).Name( "Magnometer.X" );
Map( m => m.Y ).Name( "Magnometer.Y" );
Map( m => m.Z ).Name( "Magnometer.Z" );
}
}
Gyro.X,Gyro.Y,Gyro.Z,Magnometer.X,Magnometer.Y,Magnometer.Z
1,2,3,4,5,6
```