MarcosMeli / FileHelpers

The FileHelpers are a free and easy to use .NET library to read/write data from fixed length or delimited records in files, strings or streams
https://www.filehelpers.net
MIT License
1.13k stars 349 forks source link

Dynamic Class Builder #424

Open FlemmingOstergaard opened 2 years ago

FlemmingOstergaard commented 2 years ago

I noticed that FileHelpers Dynamic is not implemented under NET 6. I have writte a ClassBuilder, that works very well with the existing Delimited/Fixed engive: The code is here, feel free to use it. It uses reflection to build the class from a simple list of fields info.

using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using FileHelpers;

namespace KBFileHelpersDynamic { public class Field { public string FieldName; public Type FieldType; public int FieldFixedLength; public QuoteMode QM = QuoteMode.AlwaysQuoted; public bool Quoted; }

public static class FileHelpersTypeBuilder {

    enum FileType { Delimited, Fixed };

    public static Type BuildClassDelimited(List<Field> _fields, string _delim = ";", string _cult = "") {
        return BuildClass(FileType.Delimited, _fields, _delim, _cult);
    }
    public static Type BuildClassFixed(List<Field> _fields, string _cult = "") {
        return BuildClass(FileType.Fixed, _fields, _cult);
    }

    private static Type BuildClass(FileType _filetype, List<Field> _fields, string _delim = ";", string _cult = "") {
        TypeBuilder tb = GetTypeBuilder();
        ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

        if(_cult == "")
            _cult = System.Globalization.CultureInfo.CurrentCulture.ToString();

        // Ignore empty lines
        Type[] attrCtorParams = new Type[0];
        var attrCtorInfo = typeof(IgnoreEmptyLinesAttribute).GetConstructor(attrCtorParams);
        CustomAttributeBuilder attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[0]);
        tb.SetCustomAttribute(attrBuilder);

        if(_filetype == FileType.Delimited) {
            // Set Type Delimited and delimiter char
            attrCtorParams = new Type[] { typeof(string), typeof(string) };
            attrCtorInfo = typeof(DelimitedRecordAttribute).GetConstructor(attrCtorParams);
            attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[] { _delim, _cult });
            tb.SetCustomAttribute(attrBuilder);
        }
        else {
            // Set Type Fixed record length
            attrCtorParams = new Type[] { typeof(FixedMode), typeof(string) };
            attrCtorInfo = typeof(FixedLengthRecordAttribute).GetConstructor(attrCtorParams);
            attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[] { FixedMode.ExactLength, _cult });
            tb.SetCustomAttribute(attrBuilder);
        }

        // NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
        foreach(var field in _fields)
            CreateProperty(_filetype, tb, field);

        return tb.CreateType();
    }

    private static TypeBuilder GetTypeBuilder() {
        var typeSignature = "MyDynamicType";
        var an = new AssemblyName(typeSignature);
        AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
        TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
                TypeAttributes.Public |
                TypeAttributes.Class |
                TypeAttributes.AutoClass |
                TypeAttributes.AnsiClass |
                TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoLayout,
                null);
        return tb;
    }

    private static void CreateProperty(FileType _ftype, TypeBuilder tb, Field _field) {
        string propertyName = _field.FieldName;
        Type propertyType = _field.FieldType;
        FieldBuilder fieldBuilder = tb.DefineField(propertyName, propertyType, FieldAttributes.Public);

        if(_ftype == FileType.Fixed) {
            Type[] attrCtorParams = new Type[] { typeof(int) };
            var attrCtorInfo = typeof(FieldFixedLengthAttribute).GetConstructor(attrCtorParams);
            var attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[] { _field.FieldFixedLength });
            fieldBuilder.SetCustomAttribute(attrBuilder);
        }

        if(_ftype == FileType.Delimited) {
            Type[] attrCtorParams = new Type[0];
            var attrCtorInfo = typeof(FieldOptionalAttribute).GetConstructor(attrCtorParams);
            var attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[0]);
            fieldBuilder.SetCustomAttribute(attrBuilder);
        }
        if(_ftype == FileType.Delimited && _field.Quoted) {             // Only if quoted at all
            Type[] attrCtorParams = new Type[] { typeof(char), typeof(QuoteMode), typeof(MultilineMode) };
            var attrCtorInfo = typeof(FieldQuotedAttribute).GetConstructor(attrCtorParams);
            var attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[] { '"', _field.QM, MultilineMode.AllowForBoth });
            fieldBuilder.SetCustomAttribute(attrBuilder);
        }
    }
}

}