如何强制.NET的XmlSerializer将 xsi:type =“FooClass”添加到 FooClass
类型的成员/节点?
该方案是当前发布的应用程序,其中包含第1版:
目标:完全删除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
{
}
答案 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序列化类。