3

.NET XmlSerializer クラスを使用して GPX ファイルを逆シリアル化しています。

GPX 標準には 2 つのバージョンがあります。

  • <gpx xmlns="http://www.topografix.com/GPX/1/0"> ... </gpx>
  • <gpx xmlns="http://www.topografix.com/GPX/1/1"> ... </gpx>

また、一部の GPX ファイルではデフォルトの名前空間が指定されていません。

  • <gpx> ... </gpx>

私のコードは 3 つのケースすべてを処理する必要がありますが、XmlSerializer で処理する方法がわかりません。

これは一般的なシナリオであり、たとえば KML にも同じ問題があるため、単純な解決策が必要であると確信しています。

4

3 に答える 3

5

これと似たようなことを以前に数回行ったことがあります。これは、少数の名前空間のみを処理する必要があり、それらすべてを事前に知っている場合に役立つ可能性があります。クラスの単純な継承階層を作成し、さまざまな名前空間のさまざまなクラスに属性を追加します。次のコード サンプルを参照してください。このプログラムを実行すると、次の出力が得られます。

Deserialized, type=XmlSerializerExample.GpxV1, data=1
Deserialized, type=XmlSerializerExample.GpxV2, data=2
Deserialized, type=XmlSerializerExample.Gpx, data=3

コードは次のとおりです。

using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

[XmlRoot("gpx")]
public class Gpx {
        [XmlElement("data")] public int Data;
}

[XmlRoot("gpx", Namespace = "http://www.topografix.com/GPX/1/0")]
public class GpxV1 : Gpx {}

[XmlRoot("gpx", Namespace = "http://www.topografix.com/GPX/1/1")]
public class GpxV2 : Gpx {}

internal class Program {
    private static void Main() {
        var xmlExamples = new[] {
            "<gpx xmlns='http://www.topografix.com/GPX/1/0'><data>1</data></gpx>",
            "<gpx xmlns='http://www.topografix.com/GPX/1/1'><data>2</data></gpx>",
            "<gpx><data>3</data></gpx>",
        };

        var serializers = new[] {
            new XmlSerializer(typeof (Gpx)),
            new XmlSerializer(typeof (GpxV1)),
            new XmlSerializer(typeof (GpxV2)),
        };

        foreach (var xml in xmlExamples) {
            var textReader = new StringReader(xml);
            var xmlReader = XmlReader.Create(textReader);

            foreach (var serializer in serializers) {
                if (serializer.CanDeserialize(xmlReader)) {
                    var gpx = (Gpx)serializer.Deserialize(xmlReader);
                    Console.WriteLine("Deserialized, type={0}, data={1}", gpx.GetType(), gpx.Data);
                }
            }
        }
    }
}
于 2012-03-31T03:21:18.127 に答える
3

他の提案が出る前に私が思いついた解決策は次のとおりです。

  var settings = new XmlReaderSettings();
  settings.IgnoreComments = true;
  settings.IgnoreProcessingInstructions = true;
  settings.IgnoreWhitespace = true;
  using (var reader = XmlReader.Create(filePath, settings))
  {
    if (reader.IsStartElement("gpx"))
    {
      string defaultNamespace = reader["xmlns"];
      XmlSerializer serializer = new XmlSerializer(typeof(Gpx), defaultNamespace);
      gpx = (Gpx)serializer.Deserialize(reader);
    }
  }

この例では、任意の名前空間を受け入れますが、既知の名前空間の特定のリストを簡単にフィルター処理することができます。

于 2012-04-05T07:45:21.587 に答える
1

奇妙なことに、これをうまく解決することはできません。このトラブルシューティング記事の逆シリアル化セクションをご覧ください。特に次のように述べているところ:

逆シリアル化プロセス中に例外が発生するエラー条件はごくわずかです。最も一般的なものは次のとおりです
。 • ルート要素またはその名前空間の名前が、予期された名前と一致しませんでした。
...

これに使用する回避策は、最初の名前空間を設定し、逆シリアル化操作を試行/キャッチし、名前空間が原因で失敗した場合は、次の名前空間で試します。すべての名前空間オプションが失敗した場合にのみ、エラーをスローします。

非常に厳密な観点から、逆シリアル化する型は特定のスキーマ/名前空間を表す必要があり、別のスキーマ/名前空間からデータを読み取ることができる必要があることは意味がないため、この動作は正しいと主張できます。 . ただし、実際には、これはまったく面倒です。バージョンが変更されたときにファイル拡張子が変更されることはめったにないため、.gpx ファイルが v0 か v1 かを判断する唯一の方法は xml コンテンツを読み取ることですが、xmldeserializer はどのバージョンになるかを事前に伝えない限り変更されません。

于 2012-03-31T00:06:48.200 に答える