Csvhelper: Nested properties in header

Created on 22 Mar 2014  路  10Comments  路  Source: JoshClose/CsvHelper

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?

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, },
},
};

    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( m => m.Gyro );
References( m => m.Magnometer );
}
}

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
```

All 10 comments

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( m => m.Gyro );
References( m => m.Magnometer );
}
}

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

malinru picture malinru  路  5Comments

hellboy81 picture hellboy81  路  6Comments

GraceYuJuSong picture GraceYuJuSong  路  4Comments

Wagimo picture Wagimo  路  4Comments

NeilMeredith picture NeilMeredith  路  4Comments