2012-03-26 58 views
2

我正在使用.NET XmlSerializer類反序列化GPX文件。如何使用XmlSerializer來處理不同的命名空間版本?

有在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具有相同的問題。

+0

如果您可以發佈更完整的XML,我可以幫助您使用LinqToXml - 否則我無法幫助XmlSerializer。你可以點擊我的名字,然後搜索我的用戶和'[xml]'查看我發佈的所有內容。 – 2012-03-26 18:11:19

回答

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

這個例子接受任何名字空間,但是你可以很容易地讓它過濾一個已知名字空間的特定列表。

1

奇怪的是,你不能很好地解決這個問題。查看this疑難解答文章中的反序列化部分。尤其是它指出:

在 反序列化過程中,只有少數錯誤情況會導致異常。最常見的是:
•根元素或其名稱空間的名稱 與期望的名稱不匹配。
...

我用這個解決辦法是設置第一個命名空間,try/catch語句的反序列化操作,如果它失敗,因爲命名空間的我嘗試它與下一個。只有當所有命名空間選項失敗時,我纔會拋出錯誤。

從一個非常嚴格的角度來看,你可以認爲這種行爲是正確的,因爲你反序列化的類型應該代表一個特定的模式/名稱空間,然後它也應該能夠讀取數據另一個模式/名稱空間。但實際上這是非常惱人的。當版本發生變化時,文件擴展很少會發生變化,因此要判斷.gpx文件是v0還是v1的唯一方法是讀取xml內容,但xmldeserializer不會除非您事先告訴哪個版本。

+0

謝謝艾迪,那篇疑難解答文章非常好。你的方法與John的方法基本相似,但是他使用CanDeserialize()方法避免了try-catch塊的需要。 – MetaMapper 2012-04-05 07:37:04

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

謝謝約翰,這是一個很好的解決方案。我不知道CanDeserialize()方法。這種方法使代碼中的事情變得更好,更明確。唯一的缺點是不得不引入額外的子類。但是這些可能隱藏在Load方法的實現中,並且類型Gpx的結果可以通過上傳返回。 – MetaMapper 2012-04-05 07:29:44

相關問題