How can I force the use of an xsi:type attribute?

后端 未结 3 863
忘掉有多难
忘掉有多难 2020-12-20 07:44

How can I force .NET\'s XmlSerializer to add an xsi:type=\"FooClass\" to a member/node of type FooClass?

The scenario

相关标签:
3条回答
  • 2020-12-20 08:20

    I had no trouble producing the following:

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

    from

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

    (note the XmlType attributes are commented out. I wanted to see what happened if a namespace was specified)

    Please show the code you used, and the XML it produced.

    0 讨论(0)
  • 2020-12-20 08:29

    XmlSerializer can be pretty dumb and straightforward at times, which works to your advantage in this case. Just put it there manually:

    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 讨论(0)
  • 2020-12-20 08:35

    To support inheritance of types in other namespaces you need to use a solution similar to what Pavel Minaev suggested but with the XmlQualifiedName typed property instead of string, e.g.

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

    Will serialize (and de-serialize) correctly as:

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

    This satisfies the requirement of truly extensible polymorphic XML types, i.e. the base class can be used anywhere and later add-ons can be assigned, serialized and de-serialized correctly in both .NET and with XSD validation.

    Taking it further, you can also add a XmlSerializerNamespaces property with the XmlNamespaceDeclarationsAttribute to specify preferred prefixes and remove any unwanted namespaces, such as the xmlns:xsd (we only use xmlns:xsi) or even the XSI namespace for your non-inheriting XML serialized classes.

    0 讨论(0)
提交回复
热议问题