CsvClassMap внутри пользовательского CsvClassMap

Я использую CsvHelper. Чтобы выполнить сопоставление между моим объектом и файлом CSV, я использую CsvClassMap. Класс, который я сопоставляю сам с собой, содержит другие классы. В настоящее время для выполнения сопоставления с внутренними классами я использую ConvertUsing для CsvPropertyMap. Пример ниже. Car — мой основной объект, и он содержит Engine, а движок разбирается в отдельном методе:

public sealed class CarMapping : CsvClassMap<Car>
{
    CarMapping()
    {
        Map(m => m.Id).Name("CarId");
        Map(m => m.Color).Name("CarColor");
        Map(m => m.YearOfProduction).Name("YearOfProduction");
        Map(m => m.Engine).ConvertUsing(ParseEngine);
    }

    private Engine ParseEngine(ICsvReaderRow row)
    {
        var year = row.GetField<int>("EngineYear");
        var cc = row.GetField<int>("EngineCC");

        return new Engine() 
        {
            Year = year,
            CC = cc,
        };
    }
}

Это работает. Но чего я хотел бы добиться, так это использовать сопоставление внутри другого сопоставления. Что-то вроде (гипотетического решения) ниже:

public sealed class EngineMapping : CsvClassMap<Engine>
{
    EngineMapping()
    {
        Map(m => m.Year).Name("EngineYear");
        Map(m => m.Cc).Name("EngineCC");
    }
}

А затем используйте EngineMapping внутри CarMapping:

public sealed class CarMapping : CsvClassMap<Car>
{
    CarMapping()
    {
        Map(m => m.Id).Name("CarId");
        Map(m => m.Color).Name("CarColor");
        Map(m => m.YearOfProduction).Name("YearOfProduction");

        // Not real code. Something I would like to be able to use.
        // Use a mapping inside the current mapping
        Map(m => m.Engine).ConsumeClassMap<EngineMapping>();
    }
}

person PiotrWolkowski    schedule 31.05.2016    source источник
comment
вы можете использовать oledb для чтения вашего csv в datatable, а затем написать свой собственный синтаксический анализатор. См. следующую публикацию: stackoverflow.com /вопросы/30129406/   -  person jdweng    schedule 31.05.2016
comment
У меня нет проблем с чтением CSV и созданием объектов из файла. Это отлично работает (первый пример кода). Я получаю правильную структуру объекта с моим текущим решением. Я хочу знать, возможно ли извлечь часть сопоставления в другой класс сопоставления и повторно использовать его внутри исходного сопоставления.   -  person PiotrWolkowski    schedule 31.05.2016
comment
Ответ «да», если ваши входные данные распределены по уровням, поэтому вы знаете, когда начинаются и заканчиваются дочерние данные. В файле CSV у вас может быть два столбца с именем родителя и именем дочернего элемента (или несколько дочерних столбцов для прародителя, родителя, дочернего элемента) для создания слоев. В текстовых файлах (не csv) вы увидите строки заголовков для разделения слоев/разделов текста.   -  person jdweng    schedule 31.05.2016
comment
Извините, я не был точен. Под сопоставлением я имел в виду CsvClassMap<T> класс. Я использую этот конкретный класс в некоторых других методах. Если бы я мог получить по одному для каждого класса вместо одного огромного для основного класса, это значительно упростило бы мое решение.   -  person PiotrWolkowski    schedule 31.05.2016
comment
CsvClassMap — это сторонняя библиотека. Из того, что я вижу, нет способа разделить класс на основе входных данных CSV для создания дочернего класса. Я рекомендую использовать мой CSVRead для помещения данных в DataTable. очень легко взять данные и разобрать их на родительские/дочерние объекты. Вы можете использовать метод Linq GroupBy для разделения.   -  person jdweng    schedule 31.05.2016


Ответы (2)


То, что вы хотите, называется картой Reference в CSVHelper.

...
CarMap()
{
    Map(m => m.Id).Name("CarId");
    Map(m => m.Color).Name("CarColor");
    Map(m => m.YearOfProduction).Name("YearOfProduction");
    References<EngineMap>(m => m.Engine);

    // or if you tuck it away in the Engine class:
    //References<Engine.EngineMap>(m => m.Engine);
}

Затем у вас есть остальные в новой карте в классе Engine, как в вашем посте:

EngineMap()
{
    Map(m => m.Year).Name("EngineYear");
    Map(m => m.Cc).Name("EngineCC");
}

Если у CSV нет заголовка, поэтому вы используете индекс, используемые индексы могут быть 2 и 8, поскольку вы все еще ссылаетесь на порядок в том же CSV.

person Ňɏssa Pøngjǣrdenlarp    schedule 31.05.2016
comment
Спасибо. Есть ли способ сделать то же самое, но в списке объектов. Так, например, если бы двигатель из примера был списком двигателей, как можно было бы применить тот же подход? - person PiotrWolkowski; 01.06.2016
comment
Я должен проверить, но я думаю, вы могли бы сделать это с помощью ConvertUsing и некоторого linq - person Ňɏssa Pøngjǣrdenlarp; 01.06.2016

этот тип сопоставления более естественен в формате json, с Json.net вы можете использовать JsonConvert.SerializeObject(Object) для преобразования ваших объектов в json отформатируйте и используйте JsonConvert.DeserializeObject<T>(String); для возврата от Json к вашему объекту.

вот один пример:

Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };

string output = JsonConvert.SerializeObject(product);
//{
//  "Name": "Apple",
//  "ExpiryDate": "2008-12-28T00:00:00",
//  "Price": 3.99,
//  "Sizes": [
//    "Small",
//    "Medium",
//    "Large"
//  ]
//}

Product deserializedProduct = JsonConvert.DeserializeObject<Product>(output);

я знаю, что это не тот ответ, который вам нужен, но он слишком длинный, чтобы поместить его в качестве комментария (и у меня недостаточно репутации, чтобы комментировать).

person Bloodday    schedule 31.05.2016