Csvhelper: Constant doesn't work when there is no field in the CSV

Created on 11 May 2018  路  5Comments  路  Source: JoshClose/CsvHelper

I'm trying to write a map that always fills in one field in my output class with a constant value. However I can't, it only works when there is a field in the incoming csv file.

Here's a test for what I want to do. (an additional test in ConstantTests.cs)

        [TestMethod]
        public void ConstantNoFieldTest()
        {
            var rows = new Queue<string[]>();
            rows.Enqueue(new[] { "Id"});
            rows.Enqueue(new[] { "1"});
            rows.Enqueue(new[] { "2"});
            rows.Enqueue(null);
            var parser = new ParserMock(rows);

            var csv = new CsvReader(parser);
            csv.Configuration.RegisterClassMap<TestStringMap>();
            var records = csv.GetRecords<Test>().ToList();

            csv.Configuration.MissingFieldFound = null;

            Assert.AreEqual(1, records[0].Id);
            Assert.AreEqual("constant", records[0].Name);
            Assert.AreEqual(2, records[1].Id);
            Assert.AreEqual("constant", records[1].Name);
        }

(for reference, TestStringMap looks like:

private sealed class TestStringMap : ClassMap<Test>
        {
            public TestStringMap()
            {
                Map( m => m.Id );
                Map( m => m.Name ).Constant( "constant" );
            }
        }

)

feature

Most helpful comment

I'll put this as a feature request. When writing you don't even have to specify a property and it works. I think this might have just been an oversight.

For now, you can do this.

Map(m => m.Name).ConvertUsing((IReaderRow row) => "the constant");

All 5 comments

Why do I want to do this? I'm letting users define their own mappings in software then storing them in a database so they can use their own CSV file formats. Sometimes their file formats don't have all the fields. In that case the user wants to put in a constant, and that field won't be in the incoming CSV file.

I'll put this as a feature request. When writing you don't even have to specify a property and it works. I think this might have just been an oversight.

For now, you can do this.

Map(m => m.Name).ConvertUsing((IReaderRow row) => "the constant");

Interestingly, MemeberMap.Constant() does work if the underlying mapping is index-based. That is, with csv.Configuration.HasHeaderRecord = false, and the mapping is performed implicitly or explicitly by index.

A passing test (for Reading\ConstantTests.cs, as above):

[TestMethod]
public void ConstantNoFieldIndexMapTest()
{
    var rows = new Queue<string[]>();
    rows.Enqueue(new[] { "1" });
    rows.Enqueue(new[] { "2" });
    rows.Enqueue(null);
    var parser = new ParserMock(rows);

    var csv = new CsvReader(parser);
    csv.Configuration.RegisterClassMap<TestStringMap>(); // implicit index-map without Header
    csv.Configuration.HasHeaderRecord = false;
    var records = csv.GetRecords<Test>().ToList();

    Assert.AreEqual(1, records[0].Id);
    Assert.AreEqual("constant", records[0].Name);
    Assert.AreEqual(2, records[1].Id);
    Assert.AreEqual("constant", records[1].Name);
}

Came across this bug today as I tried to map a CSV to a POCO and add a hard-coded field. I could do it after the fact, but it just seemed cleaner to do it at the point of deserialization, where all of the other properties are set. Maybe I'll try to pick up where #1252 left off.

I needed this feature today in order to add a Guid FileId to each record as it was being imported, to identify which import file generated the record. Dealing with lots of data so enumerating the records to fix them up is not desirable.

Used this workaround:
Map(m => m.FileId).ConvertUsing(row => myGuid);

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NeilMeredith picture NeilMeredith  路  4Comments

DmitryEfimenko picture DmitryEfimenko  路  3Comments

Dushyant262 picture Dushyant262  路  4Comments

KuraiAndras picture KuraiAndras  路  5Comments

malinru picture malinru  路  5Comments