3

我正在使用 .NET XmlSerializer 类来反序列化 GPX 文件。

GPX 标准有两个版本:

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

此外,一些 GPX 文件没有指定默认命名空间:

  • <gpx> ... </gpx>

我的代码需要处理所有这三种情况,但我不知道如何让 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 回答