代码之家  ›  专栏  ›  技术社区  ›  Tim Erickson

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

  •  0
  • Tim Erickson  · 技术社区  · 16 年前

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

    该场景是当前发布的应用程序,在v.1中:

    • fooclass继承foobseclass
    • FoopropertyA在FoobaseClass上
    • FoopropertyB在Fooclass上
    • foobseclass装饰有[xmlclude(typeof(fooclass))]
    • BazClass具有foobaseClass类型的成员foo
    • 所有baz.foo集都指向一个fooclass实例
    • baz.foo的所有用法都需要foopropertyb(因此fooclass实例与foobaseclass)

    目标:完全移除foobseclass,将foobseclass的成员推到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不再将xsi:type=“fooclass”放在baz/foo和so v.1 xmlserializer上。反序列化将实例化foobasclass实例,而不是设置fooropertyB,并将其分配给父baz实例的foo属性。因此,任何检查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)可序列化或编写自定义序列化代码的情况下是不行的。不过,我愿意接受好的建议。同时我已经实现了 解决办法 下面的黑,我希望有更优雅的东西,或者至少可以让我以某种方式完全删除foobseclass。

    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 回复  |  直到 12 年前
        1
  •  3
  •   Pavel Minaev    16 年前

    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 { }
        }
    }
    
        2
  •  1
  •   John Saunders    16 年前

    我在制作以下内容时没有遇到任何问题:

    <?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。

        3
  •  0
  •   Tony Wall    12 年前

    要支持其他命名空间中类型的继承,您需要使用类似于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),甚至是用于非继承的xml序列化类的xsi命名空间。