2009-12-22 81 views
0

如何強制.NET的XmlSerializer將xsi:type =「FooClass」添加到類型爲的成員/節點FooClass如何強制使用xsi:type屬性?

的方案是一種目前發佈的應用程序,其中在第1節:

  • FooClass繼承FooBaseClass
  • FooPropertyA是FooBaseClass
  • FooPropertyB是FooClass
  • FooBaseClass裝飾有[ XmlInclude(typeof(FooClass))]
  • BazClass has FooBaseClass類型的成員Foo
  • 所有成套的Baz。美孚是一個FooClass實例
  • Baz.Foo的所有用途指望FooPropertyB(等等FooClass實例VS FooBaseClass)

的目標是:在保持刪除FooBaseClass完全推FooBaseClass成員成FooClass,反向序列化兼容性

問題:然後我失去了Baz.Foo序列化的xsi:type =「FooClass」屬性。

換句話說

public class BazClass 
{ 
    public BazClass() 
    { 
     Foo = new FooClass { A = 5, B = "Hello" }; 
    } 
    public FooClass Foo { get; set; } 
} 

public class FooClass 
{ 
    public int FooPropertyA { get; set; } 
    public string FooPropertyB { get; set; } 
} 

需求XmlSerializer.Serialize輸出是

<Baz> 
    <Foo xsi:type="FooClass"> 
     <FooPropertyA>Hello</FooPropertyA> 
     <FooPropertyB>5</FooPropertyB> 
    </Foo> 
</Baz> 

卸下FooBasClass是容易的,但隨後的XmlSerializer不再放置的xsi:type = 「FooClass」 上Baz/Foo等v.1 XmlSerializer.Deserialize實例化FooBaseClass實例,不設置FooPropertyB,並將其分配給父Baz實例的Foo屬性。因此,任何檢查Baz.Foo是FooClass還是直接強制轉換的代碼都會失敗。

的xsi:type屬性在其中是

public class BazClass 
{ 
    public BazClass() 
    { 
     Foo = new FooClass { A = 5, B = "Hello" }; 
    } 
    public FooBaseClass Foo { get; set; } 
} 

public class FooClass : FooBaseClass 
{ 
    public string FooPropertyB { get; set; } 
} 

[XmlInclude(typeof(FooClass))]  
public class FooBaseClass 
{ 
    public int FooPropertyA { get; set; } 
} 

我認爲簡單的答案是,你不能在第1節的代碼自動放置 - 至少在沒有實現我(XML)序列化或編寫自定義序列化代碼。不過,我接受很好的建議。同時我已經實施了一個變通辦法下面,並希望更優雅的東西,或者至少讓我以某種方式徹底刪除FooBaseClass。

BazClass 
{ 
    [XmlElement("Foo")] 
    public FooBaseClass XmlFoo { get { return Foo; } set { Foo = (StartPicture)value; } } 

    [XmlIgnore] 
    public FooClass Foo { get; set; } 
}  

FooClass : FooBaseClass 
{ 
    public int FooPropertyB { get; set; } 
    public string FooPropertyA { get; set; } 
} 

[XmlInclude("FooClass")] 
FooBaseClass 
{ 
} 

回答

3

XmlSerializer有時會非常笨拙而直截了當,這對您在這種情況下的優勢很有幫助。只要把它放在那裏手動:

public class FooClass 
{ 
    public int FooPropertyA { get; set; } 
    public string FooPropertyB { get; set; } 

    [XmlAttribute("type", Namespace="http://www.w3.org/2001/XMLSchema-instance")] 
    public string XsiType 
    { 
     get { return "Foo"; } 
     set { } 
    } 
} 
+0

工作就像一個魅力!第二組眼睛的值... :-) – 2009-12-22 18:20:40

+0

當類型位於另一個名稱空間時不起作用。 – 2014-05-27 14:05:21

+0

此聲明中的命名空間用於'type'屬性,而不是類型本身。如果你需要一個類型與元素本身不同的名稱空間,那麼我認爲你可以通過手動注入一個'xmlns'屬性(通過使用一個虛擬的'[XmlAttribute]'屬性)來以類似的方式破解它。 ,然後在typename中引用它。 – 2014-05-27 16:45:35

1

我產生什麼問題的:

<?xml version="1.0" encoding="utf-8"?> 
<BazClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Foo xsi:type="FooClass"> 
     <FooPropertyA>Hello</FooPropertyA> 
     <FooPropertyB>5</FooPropertyB> 
    </Foo> 
</BazClass> 

[XmlInclude(typeof(FooClass))] 
//[XmlType(TypeName = "FooBase", Namespace = "urn:namespace", IncludeInSchema = true)] 
public class FooBaseClass 
{ 
    public string FooPropertyA { get; set; } 
} 

//[XmlType(TypeName = "Foo", Namespace = "urn:namespace", IncludeInSchema = true)] 
public class FooClass : FooBaseClass 
{ 
    public int FooPropertyB { get; set; } 
} 

public class BazClass 
{ 
    public FooBaseClass Foo { get; set; } 
} 

(注意XmlType屬性被註釋掉。如果指定了命名空間,我想看看發生了什麼)

請顯示您使用的代碼以及它生成的XML。

+0

我已經(希望)澄清我的問題上面。是的,你的例子可以工作,但它不會做我想要的 - 即刪除FooBaseClass,將FooPropertyB推入FooClass。你的例子實際上反映了我目前的狀態 - 不是我想要的狀態。 – 2009-12-22 16:31:17

+0

我剛剛看到我的第一條問題實際上是相當誤導 - 我的道歉。 – 2009-12-22 16:39:57

0

爲了支持其他命名空間類型的繼承,你需要使用類似於帕維爾Minaev建議,但與XmlQualifiedName的解決方案類型的屬性,而不是字符串,例如

using System.Xml; 
using System.Xml.Schema; 
using System.Xml.Serialization; 

namespace Test 
{ 
    /// <summary> 
    /// Base class which is XML serializable and extensible. 
    /// </summary> 
    [XmlRoot(XmlRootName, Namespace = XmlRootNamespace)] 
    public abstract class BaseClassInOtherNamespace 
    { 
     /// <summary> 
     /// Name of the XML element 
     /// </summary> 
     public const string XmlRootName = "Base"; 

     /// <summary> 
     /// XML namespace in which this type is defined. 
     /// </summary> 
     public const string XmlRootNamespace = "urn:base"; 

     /// <summary> 
     /// Creates an instance which serializes as the correct inherited XML type. 
     /// </summary> 
     protected BaseClassInOtherNamespace(XmlQualifiedName xsiType) 
     { 
      XsiType = xsiType; 
     } 

     /// <summary> 
     /// XML type for serialization. 
     /// </summary> 
     [XmlAttribute("type", Namespace = XmlSchema.InstanceNamespace)] 
     public XmlQualifiedName XsiType { get; set; } 

     /// <summary> 
     /// Some base property. 
     /// </summary> 
     public int BaseProperty { get; set; } 
    } 

    /// <summary> 
    /// Inheriting class extending the base class, created in a different XML namespace. 
    /// </summary> 
    [XmlRoot(XmlRootName, Namespace = XmlRootNamespace)] 
    [XmlType(XmlTypeName, Namespace = XmlTypeNamespace)] 
    public class InheritingClass : BaseClassInOtherNamespace 
    { 
     /// <summary> 
     /// Name of the XML element 
     /// </summary> 
     public const string XmlTypeName = "Inheriting"; 

     /// <summary> 
     /// XML namespace in which this type is defined. 
     /// </summary> 
     public const string XmlTypeNamespace = "urn:other"; 

     /// <summary> 
     /// Creates an instance. 
     /// </summary> 
     public InheritingClass() : base(new XmlQualifiedName(XmlTypeName, XmlTypeNamespace)) 
     { 
     } 

     /// <summary> 
     /// Some new property in a different (inheriting) namespace. 
     /// </summary> 
     public int InheritingProperty { get; set; } 
    } 
} 

會序列化(和反序列化)正確爲:

<Base xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:q1="urn:other" xsi:type="q1:Inheriting" xmlns="urn:base"> 
    <BaseProperty>0</BaseProperty> 
    <q1:InheritingProperty>0</q1:InheritingProperty> 
</Base> 

這滿足真正可擴展的多態XML類型的要求,即基類可用於任何地方,後來的加載項可能可以在.NET和XSD驗證中正確分配,序列化和反序列化。進一步,您還可以添加XmlSerializerNamespaces屬性和XmlNamespaceDeclarationsAttribute來指定首選前綴並刪除任何不需要的名稱空間,例如xmlns:xsd(我們只使用xmlns:xsi),或者甚至爲您的非XSI命名空間 - 繼承XML序列化類。