如何将具有多态性的c#对象序列化为具有重写的soapattributes的soapmessage,类似于XmlAttributeOverrides?

如何将具有多态性的c#对象序列化为具有重写的soapattributes的soapmessage,类似于XmlAttributeOverrides?,c#,serialization,soap,C#,Serialization,Soap,我有一个动态创建的类型,因此无法更改它的实现。我不能应用像这样的解决方案 [SoapInclude()] 想象一下,如果我有这样的课程: public class BaseClass{} public class DerivedClass:BaseClass{} public class AnotherDerivedClass:BaseClass{} public class RequestClass { public BaseClass item{get;set;} } 如果我

我有一个动态创建的类型,因此无法更改它的实现。我不能应用像这样的解决方案

[SoapInclude()] 
想象一下,如果我有这样的课程:

public class BaseClass{}
public class DerivedClass:BaseClass{}
public class AnotherDerivedClass:BaseClass{}

public class RequestClass
{
    public BaseClass item{get;set;}
}
如果我们尝试简单地序列化,我们会得到一个异常,即找不到DerivedClass,因此我们必须重写xmlattributes。 我已经成功地覆盖了我的类型,并用
XmlAttributeOverrides
将其序列化,如下所示:

var requestObject = new RequestClass() {item = new DerivedClass()};
XmlAttributes attrs = new XmlAttributes();
XmlElementAttribute attr = new XmlElementAttribute
{
    ElementName = "DerivedClass",
    Type = typeof(DerivedClass)
};
attrs.XmlElements.Add(attr);
XmlAttributeOverrides attoverrides = new XmlAttributeOverrides();
attoverrides.Add(typeof(RequestClass), "item", attrs);
XmlSerializer sr = new XmlSerializer(typeof(RequestClass), attoverrides);
StringWriter writer = new StringWriter();
sr.Serialize(writer, requestObject);
var xml = writer.ToString();
现在,上面的方法可以工作了,但我想要的是将我的对象序列化为soap消息。我发现了类似于上面的类,比如
SoapAttributeOverrides
,我试图用这些类覆盖它,但它似乎不起作用。我试过这样做:

var requestObject = new RequestClass(){item = new DerivedClass()};
SoapAttributeOverrides ovr = new SoapAttributeOverrides();
SoapAttributes soapAtts = new SoapAttributes();
SoapElementAttribute element = new SoapElementAttribute();
element.ElementName = "DerivedClass";
ovr.Add(typeof(RequestClass), "item", soapAtts);
var sr = new XmlSerializer(new SoapReflectionImporter(ovr).ImportTypeMapping(typeof(RequestClass)));
StringWriter writer = new StringWriter();
sr.Serialize(writer, requestObject);
var xml = writer.ToString();
writer.Close();

我再次发现DerivedClass未找到异常。如何给
SoapElementAttribute
一个类似于
XmlElementAttribute.type
的类型,以便通过重写使序列化支持
基类的多态性?

在.Net中,SOAP编码的XML序列化通过一种机制支持多态性。要在运行时将包含的类型添加到
SoapReflectionImporter
,请使用:

因此,您可以按如下方式制造
XmlSerializer

public static XmlSerializer RequestClassSerializer { get; } 
    = CreateSoapSerializerWithBaseClassIncludedTypes(typeof(RequestClass), typeof(DerivedClass), typeof(AnotherDerivedClass));

static XmlSerializer CreateSoapSerializerWithBaseClassIncludedTypes(Type rootType, params Type [] includedTypes)
{
    var importer = new SoapReflectionImporter();
    foreach (var type in includedTypes)
        importer.IncludeType(type);
    return new XmlSerializer(importer.ImportTypeMapping(rootType));
}
var requestObject = new RequestClass(){item = new DerivedClass()};

var xml = requestObject.GetSoapXml("wrapper", RequestClassSerializer);
然后介绍以下扩展方法:

public static partial class XmlSerializationHelper
{
    public static string GetSoapXml<T>(this T obj, XName wrapperName, XmlSerializer serializer = null)
    {
        using (var textWriter = new StringWriter())
        {
            var settings = new XmlWriterSettings() { Indent = true }; // For cosmetic purposes.
            using (var xmlWriter = XmlWriter.Create(textWriter, settings))
            {
                xmlWriter.WriteStartElement(wrapperName.LocalName, wrapperName.NamespaceName);
                (serializer ?? GetDefaultSoapSerializer(obj.GetType())).Serialize(xmlWriter, obj);
                xmlWriter.WriteEndElement();
            }
            return textWriter.ToString();
        }
    }

    static readonly Dictionary<Type, XmlSerializer> cache = new Dictionary<Type, XmlSerializer>();

    public static XmlSerializer GetDefaultSoapSerializer(Type type)
    {
        lock(cache)
        {
            if (cache.TryGetValue(type, out var serializer))
                return serializer;
            return cache[type] = new XmlSerializer(new SoapReflectionImporter().ImportTypeMapping(type));
        }
    }
}
注:

  • 为了避免严重的内存泄漏,您应该静态缓存任何未使用
    XmlSerializer(Type)
    XmlSerializer(Type,String)
    构造函数创建的
    XmlSerializer
    。有关原因,请参见和

    如果要动态生成类型族,可能需要将其序列化程序缓存在受
    lock
    语句保护的静态字典中

  • 如中所述,在使用SOAP XML序列化程序直接序列化为XML之前,您需要手动编写信封或其他包装器元素

    如果出于某种原因,您只希望SOAP XML正文作为一系列XML片段,可以执行以下操作:

    public static string GetSoapBodyXml<T>(this T obj, XmlSerializer serializer = null)
    {
        using (var textWriter = new StringWriter())
        {
            var settings = new XmlWriterSettings() { Indent = true, ConformanceLevel = ConformanceLevel.Fragment }; // For cosmetic purposes.
            using (var xmlWriter = XmlWriter.Create(textWriter, settings))
            {
                xmlWriter.WriteWhitespace(""); // Hack to prevent an error about WriteStartDocument getting called for ConformanceLevel.Fragment
                (serializer ?? GetDefaultSoapSerializer(obj.GetType())).Serialize(xmlWriter, obj);
            }
            return textWriter.ToString();
        }
    }
    
    public静态字符串GetSoapBodyXml(此T对象,XmlSerializer序列化程序=null)
    {
    使用(var textWriter=new StringWriter())
    {
    var settings=new XmlWriterSettings(){Indent=true,ConformanceLevel=ConformanceLevel.Fragment};//用于修饰目的。
    使用(var xmlWriter=xmlWriter.Create(textWriter,设置))
    {
    xmlWriter.WriteWhitespace(“”;//Hack以防止为ConformanceLevel.Fragment调用WriteStartDocument时出错
    (序列化程序??GetDefaultSoapSerializer(obj.GetType()).Serialize(xmlWriter,obj);
    }
    返回textWriter.ToString();
    }
    }
    

演示小提琴。

是的,我做了类似的事情。我知道soap格式在我的案例中应该是什么样子的。所以我只是做了一件我可以静态地做的事情,其余的都很好,这是我要弄清楚的。还是要谢谢你的努力。我会将您的答案标记为正确,因为显然没有办法,我用xmlattributesoverrides序列化了c#对象本身,并将其插入soap主体中。