Чтение DBF файла в MS Dynamics AX

В MS Dynamics AX на различных проектах часто приходится заниматься импортом данных. В случае если нам нужно импортировать справочники, то в MS DAX 12 есть свой внутренний инструмент загрузки данных DMF (Data migration framework), где легко настраиваются шаблоны импорта данных и производится сам импорт. В случае если импорт данных несет периодический характер, например, импорт строк журнала ценовых соглашений или строк заказа на продажу, то реализация импорта выполняется по каким-то собственным стандартам загрузки данных либо в произвольном виде. В этих случаях как правило чаще всего используются следующие типы файлов для импорта данных:

— excel файлы,

— текстовые файлы,

— dbf файлы.

Чтение excel файлов и текстовых файлов более распространенно и не вызывает трудностей у разработчиков, а вот с чтением dbf файлов приходится поломать голову, поэтому в этой статье приведу пример чтения dbf файла.

Для того что бы импортировать данные из dbf файла нужно сначала его прочитать т.е. открыть. Для этого я создал класс DBFReader в котором создал статичный метод read:

public static System.Data.DataTable read(Filename _fileName)
{
    System.Data.DataTable resultDT = new System.Data.DataTable();
    System.IO.FileStream fileStream;
    System.Data.DataColumnCollection columnsColl;
    System.Data.DataRowCollection rowColl;
    System.Data.DataRow row;
    System.Text.Encoding encoding;
    System.String string, string2;
    System.Globalization.DateTimeFormatInfo dateTimeFormat;
    System.Globalization.NumberFormatInfo numberFormat;
    System.Globalization.CultureInfo cultureInfo;
    System.DBNull dbNull;
    System.Byte[] buffer1 = new System.Byte[4]();
    container conBuffer1;
    System.Int32 num;
    System.Byte[] buffer2 = new System.Byte[2]();
    container conBuffer2;
    System.String[] strArray1;
    System.String[] strArray2;
    System.Byte[] numArray1;
    System.Byte[] numArray2;
    System.Byte[] numArray3;
    System.Byte[] numArray4;
    int length1, length2 = 0;
    int i, j, numRows;
    int index1, index2, index3;
    str char, stringXPP;
        
    new InteropPermission(InteropKind::ClrInterop).assert();
    fileStream = new System.IO.FileStream(_fileName, System.IO.FileMode::Open);
        
    fileStream.set_Position(4);
    fileStream.Read(buffer1, 0, buffer1.get_Length());
    conBuffer1 = DBFReader::convertArray(buffer1);
    num = conPeek(conBuffer1, 1) +
            conPeek(conBuffer1, 2) * 256 +
            conPeek(conBuffer1, 3) * 65536 +
            conPeek(conBuffer1, 4) * 16777216;
        
    fileStream.set_Position(8);
    fileStream.Read(buffer2, 0, buffer2.get_Length());
    conBuffer2 = DBFReader::convertArray(buffer2);
    length1 = (conPeek(conBuffer2, 1) + conPeek(conBuffer2, 2) * 256 - 1) / 32 - 1;
    numArray1 = new System.Byte[length1]();
    numArray2 = new System.Byte[length1]();
    numArray3 = new System.Byte[32 * length1]();
    fileStream.set_Position(32);
    fileStream.Read(numArray3, 0, numArray3.get_Length());
    strArray1 = new System.String[length1]();
    strArray2 = new System.String[length1]();
        
    encoding = System.Text.Encoding::get_Default();
    for (i = 0; i < length1; ++i)
    {
        string = encoding.GetString(numArray3, i * 32, 10);
        string = string.TrimEnd(new System.Char[1]());
        strArray1.set_Item(i, string);
        string2 = System.String::Concat(System.Convert::ToChar(numArray3.get_Item(i * 32 + 11)));
        strArray2.set_Item(i, string2);
        numArray1.set_Item(i, numArray3.get_Item(i * 32 + 16));
        numArray2.set_Item(i, numArray3.get_Item(i * 32 + 17));
        j = numArray1.get_Item(i);
        length2 += j;
        columnsColl = resultDT.get_Columns();
        char = strArray2.get_Item(i);
        switch (char)
        {
            case 'L':
                columnsColl.Add(strArray1.get_Item(i), System.Type::GetType("System.Boolean"));
                break;
            case 'D':
                columnsColl.Add(strArray1.get_Item(i), System.Type::GetType("System.DateTime"));
                break;
            case 'N':
                j = numArray2.get_Item(i);
                if (j == 0)
                {
                    columnsColl.Add(strArray1.get_Item(i), System.Type::GetType("System.Int64"));
                    break;
                }
                columnsColl.Add(strArray1.get_Item(i), System.Type::GetType("System.Decimal"));
                break;
            case 'F':
                columnsColl.Add(strArray1.get_Item(i), System.Type::GetType("System.Double"));
                break;
            default:
                columnsColl.Add(strArray1.get_Item(i), System.Type::GetType("System.String"));
                break;
        }
    }
    fileStream.ReadByte();
    cultureInfo = new System.Globalization.CultureInfo("en-US", false);
    dateTimeFormat = cultureInfo.get_DateTimeFormat();
    cultureInfo = new System.Globalization.CultureInfo("en-US", false);
    numberFormat = cultureInfo.get_NumberFormat();
    numArray4 = new System.Byte[length2]();
    resultDT.BeginLoadData();
        
    encoding = System.Text.Encoding::GetEncoding(866);
    numRows = num;
    for (index1 = 0; index1 < numRows; ++index1)
    {
        fileStream.ReadByte();
        fileStream.Read(numArray4, 0, numArray4.get_Length());
        row = resultDT.NewRow();
        index2 = 0;
        for (index3 = 0; index3 < length1; ++index3)
        {
            string = encoding.GetString(numArray4, index2, numArray1.get_Item(index3));
            string = string.TrimEnd(new System.Char[1]());
            j = numArray1.get_Item(index3);
            index2 += j;
            stringXPP = string;
            if (stringXPP != '')
            {
                char = strArray2.get_Item(index3);
                switch (char)
                {
                    case 'L':
                        row.set_Item(index3, stringXPP == 'T' ? true : false);
                        continue;
                    case 'D':
                        row.set_Item(index3, System.DateTime::ParseExact(string, 'yyyyMMdd', dateTimeFormat));
                        continue;
                    case 'N':
                        j = numArray2.get_Item(index3);
                        if (j != 0)
                            row.set_Item(index3, System.Decimal::Parse(string, numberFormat));
                        else
                            row.set_Item(index3, System.Int64::Parse(string, numberFormat));
                        continue;
                    case 'F':
                        row.set_Item(index3, System.Double::Parse(string, numberFormat));
                        continue;
                    default:
                        row.set_Item(index3, string);
                        continue;
                }
            }
            else
                row.set_Item(index3, '');
        }
        rowColl = resultDT.get_Rows();
        rowColl.Add(row);
    }
    resultDT.EndLoadData();
    fileStream.Close();
        
    return resultDT;
}

Для того чтобы байт массивы конвертировать в тип контейнер добавил метод convertArray:

public static container convertArray(System.Byte[] _mas)
{
    container resCon;
    int i, num, lengthMas = _mas.get_Length();
        
    for (i = 1; i <= lengthMas; i++)
    {
        num    = _mas.get_Item(i - 1);
        resCon += [num];
    }
        
    return resCon;
}

Теперь когда файл открыт нужно его построчно перебрать, т.е. вытащить данные из файла. Для этого я создал класс DBFReaderList, структура которого аналогична структуре классов Emunerate. Ниже приведен листинг кода класса.

public class DBFReaderList
{
    System.Data.DataTable dataTable;
    System.Data.DataRowCollection rowCollection;
    System.Data.DataRow row;
    int num;
}
      
public System.Data.DataRow current()
{
    if (row)
        return row;
    else
        return null;
}
      
public anytype currentItemColumn(str _nameCol)
{
    System.Object obj;
        
    if (_nameCol && row)
    {
        obj = row.get_Item(_nameCol);
        return DBFReaderList::convertValueToXppType(obj);
    }
    else
        return null;
}
      
public boolean moveNext()
{
    int countLines = rowCollection.get_Count();
        
    if (num >= 0 && rowCollection && countLines > num)
    {
        row = rowCollection.get_Item(num);
        num++;
        return true;
    }
        
    return false;
}
      
public void new(System.Data.DataTable _dataTable)
{
    dataTable = _dataTable;
    if (dataTable)
        rowCollection = dataTable.get_Rows();
    num = 0;
}
      
public void reset()
{
    num = 0;
}
      
public static DBFReaderList construct(System.Data.DataTable _dataTable)
{
    return new DBFReaderList(_dataTable);
}
      
private static anytype convertValueToXppType(System.Object _value)
{
    System.Boolean boolValue;
    boolean boolValueXpp;
    System.DateTime dateValue;
    TransDate dateValueXpp;
    System.Int64 intValue;
    int64 intValueXpp;
    System.Decimal decimalValue;
    System.Double doubleValue;
    real realValueXpp;
    System.String stringValue;
    str stringValueXpp;
    System.Type typeValue = _value.GetType();
    str stringType = typeValue.ToString();
        
    switch (stringType)
    {
        case 'System.Boolean':
            boolValue = System.Convert::ToBoolean(_value);
            boolValueXpp = boolValue;
            return boolValueXpp;
        case 'System.DateTime':
            dateValue = System.Convert::ToDateTime(_value);
            dateValueXpp = dateValue;
            return dateValueXpp;
        case 'System.Int64':
            intValue = System.Convert::ToInt64(_value);
            intValueXpp = intValue;
            return intValueXpp;
        case 'System.Decimal':
            decimalValue = System.Convert::ToDecimal(_value);
            realValueXpp = decimalValue;
            return realValueXpp;
        case 'System.Double':
            doubleValue = System.Convert::ToDouble(_value);
            realValueXpp = doubleValue;
            return realValueXpp;
        default:
            stringValue = System.Convert::ToString(_value);
            stringValueXpp = stringValue;
            return stringValueXpp;
    }
}

Ну и в конечном итоге пример как работать с данными классами:

DBFReaderList readerList;
str valueSearch;

try { readerList = DBFReaderList::construct(DBFReader::read(fileName)); }
catch { throw error('Неудалось открыть файл. Файл поврежден или не существует'); }

try
{
    while (readerList.moveNext())
    {
        valueSearch = readerList.currentItemColumn(‘columnName’);
        info(valueSearch);
    }
}
catch
{
    error('Ошибка чтения файла');
}

Сначала мы открываем файл с помощью класса DBFReader, после чего с помощью класса DBFReaderList производится перебор строк через цикл while, где получаем значение конкретной ячейки из файла. Таким вот образом происходит чтение dbf файла, как оказывается ничего сложного.

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar