Using metadata from Json.NET's own contract resolver you can configure a name mapping from an IDataRecord to properties of your type MyClass. Having done that, you can map from any IDataReader including the one returned by DataTable.CreateDataReader() to a list of instances of your type.
First add the following extension method:
public static class AutomapperJsonExtensions
{
static readonly IContractResolver defaultResolver = new JsonSerializer().ContractResolver;
public static void CreateJsonDataReaderMap<TDestination>(this IMapperConfigurationExpression cfg, IContractResolver resolver = null)
{
resolver = resolver ?? defaultResolver;
var contract = resolver.ResolveContract(typeof(TDestination)) as JsonObjectContract ?? throw new ArgumentException(string.Format("{0} is not a JSON object.", typeof(TDestination)));
var map = cfg.CreateMap<IDataRecord, TDestination>();
foreach (var p in contract.Properties.Where(p => !p.Ignored && p.Writable))
{
// Map PropertyName in reader to UnderlyingName in TDestination
map.ForMember(p.UnderlyingName, opt => opt.MapFrom(r => r[p.PropertyName]));
}
}
}
Now you can configure a MapperConfiguration and map your table to a List<MyClass> as follows:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateJsonDataReaderMap<MyClass>();
});
var mapper = config.CreateMapper();
using var reader = table.CreateDataReader();
var result = mapper.Map<List<MyClass>>(reader);
Notes:
Error handling for properties present in the table but not in the class, or vice versa, has not been implemented, but presumably could be.
If your table was created using camel-cased column names you can pass an instance of CamelCasePropertyNamesContractResolver as the second argument to CreateJsonDataReaderMap() and names should map correctly.
Demo fiddle here which uses AutoMapper version 9.