2

私は最近、protobuf-netを使用して、アプリケーションが使用する大きなプレーン テキスト データ ファイルをシリアル化し、プレーン テキスト データの解析よりもパフォーマンスが向上するかどうかを確認することにしました。

データクラス:

[ProtoContract]
public class ClimateFile : BaseModel
{
    #region Properties
    [ProtoMember(1)]
    public double Latitude { get { return GetValue(() => Latitude); } private set { SetValue(() => Latitude, value); } }

    [ProtoMember(2)]
    public double Longitude { get { return GetValue(() => Longitude); } private set { SetValue(() => Longitude, value); } }

    [ProtoMember(3)]
    public string Notes { get { return GetValue(() => Notes); } set { SetValue(() => Notes, value); } }

    [ProtoMember(4)]
    public DateTime MinDate { get { return GetValue(() => MinDate); } private set { SetValue(() => MinDate, value); } }

    [ProtoMember(5)]
    public DateTime MaxDate { get { return GetValue(() => MaxDate); } private set { SetValue(() => MaxDate, value); } }

    [ProtoMember(6)]
    public List<ClimateDailyData> DailyData { get { return GetValue(() => DailyData); } private set { SetValue(() => DailyData, value); } }
    #endregion

    #region Method
    public static ClimateFile Load(string filePath)
    {
        try
        {
            var ext = Path.GetExtension(filePath).ToUpper();
            if (ext == ".P51")
                return new ClimateFile(filePath);
            else if (ext == ".P51X")
            {
                ClimateFile climateFile;
                using (var file = File.OpenRead(filePath))
                {
                    climateFile = Serializer.Deserialize<ClimateFile>(file);
                }
                return climateFile;
            }
        }
        catch (Exception e)
        {
            throw new InvalidDataException(String.Format("The file \"{0}\" could not be opened, {1}", filePath, ExceptionUtilities.JoinExceptionMessages(e)));
        }

        throw new ArgumentException("filePath was not a .P51 or .P51X file.");
    }

    public void LoadP51File(string filePath)
    {
        List<string[]> lines = GetFileLines(filePath);
        Latitude = double.Parse(lines[0][0]);
        Longitude = double.Parse(lines[0][1]);
        for (int i = 2; i < lines[0].Length; ++i)
            Notes += String.Format("{0} ", lines[0][i]);
        MinDate = GetDate(lines[2][0]);
        MaxDate = GetDate(lines[lines.Count - 1][0]);

        // Parse daily data
        lines.RemoveRange(0, 2);
        var dailyData = lines.Select(row => new ClimateDailyData()
            {
                DataDate = GetDate(row[0]),
                MaxTemperature = double.Parse(row[2]),
                MinTemperature = double.Parse(row[3]),
                Rainfall = double.Parse(row[4]),
                Evaporation = double.Parse(row[5]),
                SolarRadiation = double.Parse(row[6])
            }).ToList();
        DailyData = dailyData;
    }

    public void SaveP51XFile(string filePath)
    {
        using (var file = File.Create(filePath))
        {
            Serializer.Serialize<ClimateFile>(file, this);
        }
    }

    public List<string[]> GetFileLines(string filePath)
    {
        var rows = new List<string[]>();
        var cells = new List<string>();
        var cell = new StringBuilder();
        using (var fs = new BufferedStream(File.OpenRead(filePath)))
        {
            int buffer;
            while ((buffer = fs.ReadByte()) != -1)
            {
                char nextChar = (char)buffer;

                if (nextChar >= '!') // If the character is a non-whitespace printable character
                {
                    cell.Append(nextChar);
                    continue;
                }

                if (nextChar == ' ' || nextChar == '\t')
                {
                    if (cell.Length > 0)
                    {
                        cells.Add(cell.ToString());
                        cell.Clear();
                    }
                    continue;
                }

                if (nextChar == '\r' || nextChar == '\n')
                {
                    if (cell.Length > 0)
                    {
                        cells.Add(cell.ToString());
                        cell.Clear();
                    }
                    if (cells.Count > 0)
                    {
                        rows.Add(cells.ToArray());
                        cells.Clear();
                    }
                    continue;
                }
                throw new InvalidDataException("The climate file contains unknown characters.");
            }

            if (cell.Length > 0)
                cells.Add(cell.ToString());
            if (cells.Count > 0)
                rows.Add(cells.ToArray());
        }
        return rows;
    }

    public DateTime GetDate(string date)
    {
        return DateTime.ParseExact(date, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
    }

    public override void Dispose()
    {
        if (Disposed)
            return;
        if (DailyData != null)
            foreach (ClimateDailyData dailyData in DailyData)
                dailyData.Dispose();
        base.Dispose();
    }
    #endregion

    #region Initialization
    public ClimateFile()
    {
    }

    public ClimateFile(string filePath)
    {
        LoadP51File(filePath);
    }
    #endregion
}

コレクション データ クラス:

    [ProtoContract]
public class ClimateDailyData : BaseModel
{
    [ProtoMember(1)]
    public DateTime DataDate { get { return GetValue(() => DataDate); } set { SetValue(() => DataDate, value); } }

    [ProtoMember(2)]
    public int JulianDay { get { return DataDate.DayOfYear; } }

    [ProtoMember(3)]
    public double MaxTemperature { get { return GetValue(() => MaxTemperature); } set { SetValue(() => MaxTemperature, value); } }

    [ProtoMember(4)]
    public double MinTemperature { get { return GetValue(() => MinTemperature); } set { SetValue(() => MinTemperature, value); } }

    [ProtoMember(5)]
    public double Rainfall { get { return GetValue(() => Rainfall); } set { SetValue(() => Rainfall, value); } }

    [ProtoMember(6)]
    public double Evaporation { get { return GetValue(() => Evaporation); } set { SetValue(() => Evaporation, value); } }

    [ProtoMember(7)]
    public double SolarRadiation { get { return GetValue(() => SolarRadiation); } set { SetValue(() => SolarRadiation, value); } }
}

シリアル化コマンド:

public ICommand ImportP51
    {
        get
        {
            return new RelayCommand(delegate
                {
                    OpenFileDialog openDialogue = new OpenFileDialog()
                    {
                        AddExtension = true,
                        CheckPathExists = true,
                        CheckFileExists = true,
                        DefaultExt = ".p51",
                        Filter = "Climate Files|*.p51",
                        InitialDirectory = DataPath,
                        Multiselect = false,
                        Title = "Select a File to Open"
                    };

                    if ((bool)openDialogue.ShowDialog())
                    {
                        string filePath = String.Empty;
                        try
                        {
                            filePath = openDialogue.FileName;
                            var climate = new Climate();
                            climate.FilePath = filePath;
                            var savePath = String.Format("{0}\\{1}.p51x", ClimateDataPath, Path.GetFileNameWithoutExtension(filePath));
                            climate.FileData.SaveP51XFile(savePath);
                        }
                        catch (Exception e)
                        {
                            MessageBox.Show(String.Format("The file \"{0}\" could not be opened, {1}", filePath, ExceptionUtilities.JoinExceptionMessages(e)), "Invalid File");
                        }
                    }
                });
        }
    }

を実行するImportP51と、「指定されたメソッドはサポートされていません」という例外が発生します。2012 年 12 月 10 日にリリースされた protobuf-net V2 r594 を使用しており、取得できない場合は以前のバージョンのいずれかを使用しようと考えています。このバージョンで動作します。誰かが私が間違っていることを教えてもらえますか?

4

1 に答える 1

3

BaseModel自動的に実装されたプロパティを使用し、リスト ( DailyData) の初期化子を追加し、google-code から r594 を明示的に参照することによって実行したコードの作業バージョンを再構築しようとしました (私はあなたの を持っていないため)。私が得る唯一のエラーは次のとおりです。

プロパティ ClimateDailyData.JulianDay に変更を適用できません

データを読み取り専用プロパティに正しく逆シリアル化できないため、これは理にかなっています。そのため、そのプロパティからシリアル化マーカーを削除しました。

// [ProtoMember(2)] removed - serialized implicitly via DataData
public int JulianDay { get { return DataDate.DayOfYear; } }

その後はうまくいきました。私はあなたのコマンドフレームワークを持っていないので、私は持っています:

static class Program
{
    static void Main()
    {
        var climateFile = new ClimateFile();
        climateFile.InitSomeDataForSerialization();

        climateFile.SaveP51XFile("foo.P51X");
        var clone = ClimateFile.Load("foo.P51X");
    }
}

ここInitSomeDataForSerializationでは、いくつかの値を設定するだけです(プライベートセッターがあるため、 ではできませんMain):

public void InitSomeDataForSerialization()
{
    Longitude = 10; Latitude = 4; Notes = "Test";

    DailyData.Add(
        new ClimateDailyData { DataDate = DateTime.Today, MinTemperature = 12, MaxTemperature = 35}
    );
}

そして...それは動作します。

直感で、「Full」ではなく「CoreOnly」を参照するとどうなるかを確認しましたが、存在しないためSerializer.Serialize<T>、コンパイルが拒否されます。Serializer.Deserialize<T>

喜んでお手伝いさせていただきますが、私が知る限り、何も問題はありません。

次に提案すること:

  • 内部例外を含む、この例外で発生したことの完全なスタック トレースを表示します (以下の修正を参照)。
  • 594 zip で参照したファイルを正確に確認してください(「What Files Do I Need.txt」を参照してください。ただし、正しいファイルは「Full/net30/...」であると思います。nuget パッケージは、最も適切なファイルを自動的に)
  • 完全に再現可能な例を示してください。つまり、押しF5て正確な例外を確認できる場所を示してください (コンパイルするために例のコードを変更する必要がありました。つまり、同じことをテストしていないということです)。

正しい例外ラッピングの修正:

catch (Exception e)
{
    throw new InvalidDataException(String.Format("The file \"{0}\" could not be opened", filePath), e);
}
于 2012-10-23T06:35:27.873 に答える