2017-08-30 204 views
0

我有一個簡單的.Net框架C#控制檯應用程序,它序列化派生類型的類,其中屬性也是派生類型。序列化派生類型派生類型屬性的數組

派生類的名稱與基類相同,但位於不同的名稱空間中以防止它們發生衝突。看起來雖然XmlSerializer使用的反射不適用於此。也許有一些方法可以使用漂亮的名字(因爲在使用時它是一個DLL接口)和XML也使用漂亮的名字(因爲它將是人爲可編輯的),所以我仍然可以用一些方法來標記屬性。 。派生類的漂亮名字不是必需的(雖然會是獎金)。

的XML將有希望的樣子:

<?xml version="1.0" encoding="utf-8"?> 
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Details> 
    <Detail> 
     <Description>bald</Description> 
    </Detail> 
    <Detail> 
     <Description>red tie</Description> 
    </Detail> 
    </Details> 
</Person> 

但我可以不例外最接近的是其中<Detail>元素

<Detail xsi:type="DerivedDetail"> ... </Detail> 

不必添加此xs:type屬性是不是最好的人爲編輯的XML。

這是通過下面的C#代碼實現的。如果我刪除標記XmlType屬性,那麼該元素應該連載沒有xsi:type屬性,而是我得到一個異常:

出現InvalidOperationException:類型「Test.Detail」和「Test.Xml.Detail」都使用命名空間''中的XML類型名稱'Detail'。使用XML屬性爲類型指定唯一的XML名稱和/或名稱空間。

我試圖標誌着派生Xml.Detail類作爲一個匿名XML類型,但隨後的例外規定:

出現InvalidOperationException:不能包含匿名類型「Test.Xml.Detail」。

我已經閱讀了很多類似的問題,但還沒有遇到任何解決這個問題的東西。

在下面的代碼中Person是一個抽象類,其屬性是抽象類型Detail的數組。這些類型分別由Xml.PersonXml.Detail派生。該程序創建一個測試Xml.Person對象,並嘗試對其進行序列化:

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

namespace Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Create test details array 
      var TestDetails = new Xml.Detail[] 
      { 
       new Xml.Detail 
       { 
        Description = "bald" 
       }, 
       new Xml.Detail 
       { 
        Description = "red tie" 
       } 
      }; 

      // create test person object that holds details array 
      var TestBar = new Xml.Person() 
      { 
       Details = TestDetails 
      }; 

      // serialize the person object 
      var s = new Xml.Serializer(); 
      var TestOutput = s.Serialize(TestBar); 

      Console.WriteLine(TestOutput); 
     } 
    } 

    // base classes 
    public abstract class Person 
    { 
     public abstract Detail[] Details { get; set; } 
    } 

    public abstract class Detail 
    { 
     public abstract string Description { get; set; } 
    } 

    namespace Xml 
    { 
     // derived classes 
     [Serializable] 
     [XmlType(AnonymousType = true)] 
     [XmlRoot(IsNullable = false)] 
     public class Person : Test.Person 
     { 
      [XmlArrayItem("Detail", typeof(Detail))] 
      [XmlArray(IsNullable = false)] 
      public override Test.Detail[] Details { get; set; } 
     } 

     // This attribute makes serialization work but also adds the xsi:type attribute 
     [XmlType("DerivedDetail")] 
     [Serializable] 
     public class Detail : Test.Detail 
     { 
      public override string Description { get; set; } 
     } 

     // class that does serializing work 
     public class Serializer 
     { 
      private static XmlSerializer PersonSerializer = 
       new XmlSerializer(typeof(Person), new Type[] { typeof(Detail) }); 

      public string Serialize(Test.Person person) 
      { 
       string Output = null; 
       var Stream = new MemoryStream(); 
       var Encoding = new UTF8Encoding(false, true); 

       using (var Writer = new XmlTextWriter(Stream, Encoding)) 
       { 
        Writer.Formatting = Formatting.Indented; 
        PersonSerializer.Serialize(Writer, person); 
        Output = Encoding.GetString(Stream.ToArray()); 
       } 
       Stream.Dispose(); 
       return Output; 
      } 
     } 
    } 
} 
+0

注意周圍試圖冠名的其他方式,命名派生類爲'DerivedDetail'並移除'XmlType'屬性,仍然導致了類似'XSI:在序列化元素 – Toby

回答

1

不知道爲什麼你正在使用的,而不是接口的基類,當你沒有任何成員字段。無論如何,我假設你想Xml.Person是抽象Person或從抽象Person派生的任何類的具體實例化,而不用裝飾具有XML屬性的摘要Person。我通過強制抽象Person在序列化之前成爲Xml.Person的具體實例化來完成此操作。請用Test替換XmlSerializationProject

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

namespace XmlSerializationProject 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Create test details array 
      var TestDetails = new Xml.Detail[] 
      { 
       new Xml.Detail 
       { 
        Description = "bald" 
       }, 
       new Xml.Detail 
       { 
        Description = "red tie" 
       } 
      }; 

      // create test person object that holds details array 
      var TestBar = new Xml.Person() 
      { 
       Details = TestDetails 
      }; 

      // serialize the person object 
      var s = new Xml.Serializer(); 
      var TestOutput = s.Serialize(TestBar); 

      Console.WriteLine(TestOutput); 
      Console.ReadKey(); 
     } 
    } 

    // base classes 
    public abstract class Person 
    { 
     public abstract Detail[] Details { get; set; } 
    } 

    public abstract class Detail 
    { 
     public abstract string Description { get; set; } 
    } 

    namespace Xml 
    { 
     [Serializable] 
     [XmlType(AnonymousType = true)] 
     [XmlRoot(IsNullable = false)] 
     public class Person : XmlSerializationProject.Person 
     { 
      public Person() 
      { } 

      public Person(XmlSerializationProject.Person person) 
      { 
       // Deep copy 
       if (person.Details == null) return; 
       this.Details = new Detail[person.Details.Length]; 
       for (int i = 0; i < person.Details.Length; i++) 
       { 
        this.Details[i] = new Detail { Description = person.Details[i].Description }; 
       } 
      } 

      [XmlArray(ElementName = "Details")] 
      [XmlArrayItem("Detail", typeof(Detail))] 
      [XmlArrayItem("ODetail", typeof(XmlSerializationProject.Detail))] 
      public override XmlSerializationProject.Detail[] Details 
      { 
       get; 
       set; 
      } 
     } 

     [Serializable] 
     public class Detail : XmlSerializationProject.Detail 
     { 
      public override string Description { get; set; } 
     } 

     // class that does serializing work 
     public class Serializer 
     { 
      private static readonly XmlSerializer PersonSerializer; 

      private static Serializer() 
      { 
       var xmlAttributeOverrides = new XmlAttributeOverrides(); 
       // Change original "Detail" class's element name to "AbstractDetail" 
       var xmlAttributesOriginalDetail = new XmlAttributes(); 
       xmlAttributesOriginalDetail.XmlType = new XmlTypeAttribute() { TypeName = "AbstractDetail" }; 
       xmlAttributeOverrides.Add(typeof(XmlSerializationProject.Detail), xmlAttributesOriginalDetail); 

       // Ignore Person.Details array 
       var xmlAttributesOriginalDetailsArray = new XmlAttributes(); 
       xmlAttributesOriginalDetailsArray.XmlIgnore = true; 
       xmlAttributeOverrides.Add(typeof(XmlSerializationProject.Person), "Details", xmlAttributesOriginalDetailsArray); 

       PersonSerializer = new XmlSerializer(
        typeof(Person), xmlAttributeOverrides, new Type[] { typeof(Detail) }, new XmlRootAttribute(), "default"); 
      } 

      public string Serialize(XmlSerializationProject.Person person) 
      { 
       return Serialize(new Person(person)); 
      } 

      public string Serialize(Person person) 
      { 
       string Output = null; 
       var Stream = new MemoryStream(); 
       var Encoding = new UTF8Encoding(false, true); 

       using (var Writer = new XmlTextWriter(Stream, Encoding)) 
       { 
        Writer.Formatting = Formatting.Indented; 
        PersonSerializer.Serialize(Writer, person); 
        Output = Encoding.GetString(Stream.ToArray()); 
       } 
       Stream.Dispose(); 
       return Output; 
      } 
     } 
    } 
} 
+0

TBH接口type'屬性對我更有意義,但卻無法讓他們工作。所有我能找到的例子都使用了繼承,所以我轉而使用了繼承。你的代碼很棒!一件小事雖然; Serializer類中有*'private' *'static'構造函數?靜態構造函數IIRC不允許訪問修飾符。 – Toby

+1

不得不看一下。你是對的,但它編譯。我猜靜態構造函數是私人的,如果你考慮它。 – WithMetta

+0

它編譯?哇,我的VS2017甚至沒有那麼遠,立即發現語法錯誤,我注意到了。我刪除了「私人」,它雖然工作正常。 – Toby