2009-12-22 195 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序列化类。