Как я могу использовать XmlSerializer для обработки разных версий пространства имен? - PullRequest
2 голосов
/ 26 марта 2012

Я использую класс .NET XmlSerializer для десериализации файлов GPX.

Существует две версии стандарта GPX:

  • ...
  • ...

Кроме того, некоторые файлы GPX не указывают пространство имен по умолчанию:

  • ...

Мой код должен обрабатывать все три случая, но я не могу понять, как заставить XmlSerializer сделать это.

Я уверен, что должно быть простое решение, потому что это распространенный сценарий, например, KML имеет ту же проблему.

Ответы [ 3 ]

5 голосов
/ 31 марта 2012

Я делал нечто подобное ранее несколько раз, и это может пригодиться вам, если вам нужно иметь дело только с небольшим количеством пространств имен и вы знаете их все заранее.Создайте простую иерархию наследования классов и добавьте атрибуты в разные классы для разных пространств имен.Смотрите следующий пример кода.Если вы запустите эту программу, она выдаст:

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);
                }
            }
        }
    }
}
3 голосов
/ 05 апреля 2012

Вот решение, которое я придумал до того, как поступили другие предложения:

  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);
    }
  }

В этом примере принимается любое пространство имен, но вы можете легко настроить фильтр для определенного списка известных пространств имен.

1 голос
/ 31 марта 2012

Как ни странно, вы не можете решить это красиво.Ознакомьтесь с разделом десериализации в этой статье .Особенно там, где говорится:

Лишь несколько ошибок приводят к исключениям в процессе десериализации.Наиболее распространенные из них:
• Имя корневого элемента или его пространства имен не соответствует ожидаемому имени.
...

Обходное решение, которое я использую для этого, заключается в установкепервое пространство имен, попробуйте / поймайте операцию десериализации, и если она не удалась из-за пространства имен, я пробую ее со следующим.Только если все параметры пространства имен потерпят неудачу, я выдам ошибку.

С очень строгой точки зрения вы можете утверждать, что это поведение является правильным, поскольку тип, который вы десериализуете, должен представлять конкретную схему / пространство имен, а затем это нене имеет смысла, что он также должен иметь возможность читать данные из другой схемы / пространства имен.На практике это очень раздражает.Расширение файла редко изменяется при изменении версий, поэтому единственный способ определить, является ли файл .gpx v0 или v1, - это прочитать содержимое xml, но xmldeserializer не сможет, если вы заранее не укажете, какая версия будет.

...