データ交換に使用するフォーマットはCSV, TSV, さらにXML形式などが採用されていると思いますが、ホスト(というよりCOBOL?)とデータをやり取りする場合など、固定長レコードファイルからデータを読み込んだり、データを出力する必要がたくさんあると思います。今回は.NETで固定文字列長のデータからシリアライズし、クラスのインスタンスを作成し、またインスタンスからデータのシリアライズするクラスライブラリを作成しました。

開発環境は Visaul Studio 2008 ですが、動作環境はいまのところ、 .NET 2.0 以上です。

1. ダウンロード

ファイルは下記リンクをクリックしてダウンロードして下さい。Vista以降のOSを使用している場合は、ダウンロードしたzipファイルのプロパティ画面でブロック解除をすることを忘れないで下さい。

バージョン 1.0
ソース + DLL FixedLengthRecord_v1.0_src_dll.zip
DLL のみ FixedLengthRecord_v1.0_dll.zip

 2. 使い方

固定長として出力するには、名前空間 Handcraft.FixedLengthRecord の FixedLengthFieldAttribute と FixedLengthRecordSerializer を使用します。固定文字列長のファイルを表すクラスのプロパティにFixedLengthFieldAttribute を付与します。 FixedLengthFieldAttribute を付与したクラスに対して、FixedLengthRecordSerializer.Serializeを呼び出すとシリアライズを実施し、Deserializeを呼び出すと、ファイルからデシリアライズを行います。末尾に改行を含めるかの設定は、FixedLengthRecordSettingsクラスのインスタンスを FixedLengthRecordSerializer のコンストラクタに指定してシリアライザを初期化します。

3. サンプルプログラム

全銀データ編集ツールで使用されている固定文字列長ファイルシリアライズライブラリのサンプルを掲載します。次のサンプルコードは FixedLengthFieldAttribute をアノテートしたクラスです。

using Handcraft.FixedLengthRecord;

namespace Zengin.Model
{
    public class DataEntity
    {
        private string _dataType = "2";
        /// <summary>
        /// データ区分
        /// </summary>
        [FixedLengthField(0,1)]
        public string DataType
        {
            get { return _dataType; }
        }
        private string _destinationFinantialInstitutionCode = string.Empty;
        [FixedLengthField(1,4)]
        public string DestinationFinantialInstitutionCode
        {
            get { return _destinationFinantialInstitutionCode; }
            set { _destinationFinantialInstitutionCode = value; }
        }
        private string _destinationFinantialInstitutionName = string.Empty;
        [FixedLengthField(2,15)]
        public string DestinationFinantialInstitutionName
        {
            get { return _destinationFinantialInstitutionName; }
            set { _destinationFinantialInstitutionName = value; }
        }
        private string _destinationFinantialInstitutionBranchNumber = string.Empty;
        [FixedLengthField(3, 3)]
        public string DestinationFinantialInstitutionBranchNumber
        {
            get { return _destinationFinantialInstitutionBranchNumber; }
            set { _destinationFinantialInstitutionBranchNumber = value; }
        }
        private string _destinationFinantialInstitutionBranchName = string.Empty;
        [FixedLengthField(4,15)]
        public string DestinationFinantialInstitutionBranchName
        {
            get { return _destinationFinantialInstitutionBranchName; }
            set { _destinationFinantialInstitutionBranchName = value; }
        }
        private string _billExchangeNumber = string.Empty;
        [FixedLengthField(5,4)]
        public string BillExchangeNumber
        {
            get { return _billExchangeNumber; }
            set { _billExchangeNumber = value; }
        }
        private string _destinationAccountType = string.Empty;
        [FixedLengthField(6,1)]
        public string DestinationAccountType
        {
            get { return _destinationAccountType; }
            set { _destinationAccountType = value; }
        }
        private string _destinationAccountNumber = string.Empty;
        [FixedLengthField(7, 7, TrimOnDesirialize = false, FillUpChar = '0', IsLeftPad = false)]
        public string DestinationAccountNumber
        {
            get { return _destinationAccountNumber; }
            set { _destinationAccountNumber = value; }
        }
        private string _destinationClientName = string.Empty;
        [FixedLengthField(8,30)]
        public string DestinationClientName
        {
            get { return _destinationClientName; }
            set { _destinationClientName = value; }
        }
        private decimal _payment = 0;
        [FixedLengthField(9,10, IsLeftPad = false, FillUpChar = '0', TrimOnDesirialize = false)]
        public Decimal Payment
        {
            get { return _payment; }
            set { _payment = value; }               
        }
        private string _newCode = string.Empty;
        [FixedLengthField(10,1)]
        public string NewCode
        {
            get { return _newCode; }
            set { _newCode = value; }
        }
        private string _clientCode1 = string.Empty;
        [FixedLengthField(11,10)]
        public string ClientCode1
        {
            get { return _clientCode1; }
            set { _clientCode1 = value; }
        }
        private string _clientCode2 = string.Empty;
        [FixedLengthField(12,10)]
        public string ClientCode2
        {
            get { return _clientCode2; }
            set { _clientCode2 = value; }
        }
        private string _dummy = new String(' ', 9);
        [FixedLengthField(13,9)]
        public string Dummy
        {
            get { return _dummy; }
        }
    }
}

次に、シリアライザでシリアライズ、デシリアライズを行うサンプルを掲載します。

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;
using Handcraft.FixedLengthRecord;

namespace Zengin.Model
{
    public class ZenginDataFactory
    {   // 省略
        public static ZenginData LoadFrom(string path, FixedLengthRecordSettings settings)
        {
            ZenginData data = new ZenginData();
            using (FileStream reader = File.OpenRead(path))
            {
                FixedLengthRecordSerializer serializer = new FixedLengthRecordSerializer(settings);
                serializer.RecordRead += ZenginDataFactory.ZenginDataFactory_RecordRead;
                object[] result = serializer.Deserialize(reader, typeof(HeaderEntity));
                foreach (object item in result)
                {
                    if (item is HeaderEntity)
                    {
                        data.Header = item as HeaderEntity;
                    }
                    else if (item is DataEntity)
                    {
                        data.Datas.Add(item as DataEntity);
                    }
                    else if (item is TrailerEntity)
                    {
                        data.Trailer = item as TrailerEntity;
                    }
                    else if (item is EndEntity)
                    {
                        data.End = item as EndEntity;
                    }
                }
            }
            return data;
        }
        public static ZenginData LoadFrom(string path)
        {
            return ZenginDataFactory.LoadFrom(path, CreateSetting());
        }
        public static void SaveTo(ZenginData data, string path, FixedLengthRecordSettings settings)
        {
            ArrayList datas = new ArrayList();
            datas.Add(data.Header);
            foreach (DataEntity entity in data.Datas)
            {
                datas.Add(entity);
            }
            datas.Add(data.Trailer);
            datas.Add(data.End);

            using (StreamWriter writer = new StreamWriter(path, false, Encoding.GetEncoding("Shift_JIS")))
            {
                FixedLengthRecordSerializer serializer = new FixedLengthRecordSerializer(settings);
                serializer.RecordWriting += ZenginDataFactory.ZenginDataFactory_RecordWriting;
                serializer.Serialize(writer, datas.ToArray());
            }
        }
        public static void SaveTo(ZenginData data, string path)
        {
            ZenginDataFactory.SaveTo(data, path, CreateSetting());
        }
        private static FixedLengthRecordSettings CreateSetting()
        {
            FixedLengthRecordSettings setting = new FixedLengthRecordSettings();
            if (global::ZenginEditor.Properties.Settings.Default.NewLine.Equals(string.Empty))
            {
                setting.RequireNewLine = false;
            }
            else
            {
                setting.RequireNewLine = true;
                setting.NewLine = global::ZenginEditor.Properties.Settings.Default.NewLine;
            }
            return setting;
        }
        #endregion
    }
}

全銀データ編集ツールのすべてのソースは、ツールの紹介のページから確認できます。

このクラスライブラリのいいところ

(と私が思っていることですが。)作成したクラスライブラリのシリアライズ、デシリアライズ処理はレコード長が同じならば、異なるモデルデータを一つのファイルとして取り扱えます。つまり、一つのファイルから複数の型のクラスを作成したり、ファイルへシリアライズしたりできます。その機能が必要な最たる例が全銀データファイルなので、サンプルツールとして全銀データ編集プログラムを作成しました。

また、Attributeを使用することで、Excelファイルのスプレッドシートから記載したインタフェース定義から自動生成を行うことができます。あまりいないかもしれませんが、モデルのdllからインタフェース定義書の雛形を作成できます。

カスタマイズも行いやすいようにしたつもりです。例えば、ライブラリは固定長の文字列を扱いますが、シリアライザのRecordWritingイベントを使用し、シリアライズ後の文字列をチェックし、不正文字のチェックや、バイト長のチェックなども行えるようになっています。

誤り点、改善点、説明不足などがありましたらご指摘ください。