如何强制使用xsi:type属性?

时间:2009-12-22 00:55:26

标签: .net xml-serialization

如何强制.NET的XmlSerializer将 xsi:type =“FooClass”添加到 FooClass 类型的成员/节点?

该方案是当前发布的应用程序,其中包含第1版:

  • FooClass继承FooBaseClass
  • FooPropertyA在FooBaseClass
  • FooPropertyB在FooClass上
  • FooBaseClass用[XmlInclude(typeof(FooClass))]
  • 装饰
  • BazClass的成员Foo类型为FooBaseClass
  • Baz.Foo的所有集合都是FooClass实例
  • Baz.Foo的所有用法都期望FooPropertyB(以及FooClass实例和FooBaseClass)

目标:完全删除FooBaseClass,将FooBaseClass的成员推送到FooClass,,同时保持向后序列化兼容性

问题:然后我在Baz.Foo序列化上丢失了xsi:type =“FooClass”属性。

换句话说,

的XmlSerializer.Serialize输出
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; }
}

需要

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

删除FooBasClass很简单,但是XmlSerializer不再在Baz / Foo上放置xsi:type =“FooClass”,因此v.1 XmlSerializer.Deserialize实例化FooBaseClass实例,而不是设置FooPropertyB,并将其分配给Foo属性父Baz实例。因此,任何检查Baz.Foo是FooClass还是直接转换的代码都会失败。

xsi:type属性自动放在v.1代码

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

我认为简短的回答是你不能 - 至少没有实现I(Xml)Serializable或编写自定义序列化代码。但是,我愿意接受好的建议。同时我在下面实现了一个 workaround hack,我希望有更优雅的东西,或者至少允许我以某种方式完全删除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 个答案:

答案 0 :(得分: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 { }
    }
}

答案 1 :(得分: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。

答案 2 :(得分:0)

要支持其他命名空间中类型的继承,您需要使用类似于Pavel 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验证中正确分配,序列化和反序列化附加组件。

更进一步,您还可以使用XmlNamespaceDeclarationsAttribute添加XmlSerializerNamespaces属性以指定首选前缀并删除任何不需要的命名空间,例如xmlns:xsd(我们只使用xmlns:xsi),甚至是非您的XSI名称空间继承XML序列化类。