【问题标题】:How to serialize a c# object with polymorphism to a soapmessage with overriden soapattributes, similar to XmlAttributeOverrides?如何将具有多态性的 c# 对象序列化为具有覆盖的肥皂属性的肥皂消息,类似于 XmlAttributeOverrides?
【发布时间】:2019-08-15 11:54:44
【问题描述】:

我有一个动态创建的类型,所以我无法更改它的实现。我不能应用像

这样的解决方案
[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();

现在上面的工作,但我想要将我的对象序列化为肥皂消息。我找到了与上面类似的类,例如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 的类型,以便可以通过覆盖进行序列化以支持BaseClass 的多态性?

【问题讨论】:

    标签: c# serialization soap


    【解决方案1】:

    .Net 中 SOAP 编码的 XML 序列化通过included type 机制支持多态性。为了在运行时将包含的类型添加到 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));
    }
    

    然后介绍如下扩展方法:

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

    您将能够按如下方式序列化您的requestObject

    var requestObject = new RequestClass(){item = new DerivedClass()};
    
    var xml = requestObject.GetSoapXml("wrapper", RequestClassSerializer);
    

    注意事项:

    • 为避免严重的内存泄漏,您应该静态缓存任何不是使用XmlSerializer(Type)XmlSerializer(Type, String) 构造函数创建的XmlSerializer。原因请参见the documentationMemory Leak using StreamReader and XmlSerializer

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

    • Extension method to serialize generic objects as a SOAP formatted streamInvalidOperationException while SOAP serialization of complex type 中所述,您需要在使用 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();
          }
      }
      

    演示小提琴here.

    【讨论】:

    • 是的,我做了类似的事情。我知道肥皂格式在我的情况下应该是什么样子。所以我只是做了我可以静态做的事情,剩下的事情是我自己想办法。仍然感谢您的努力。我会将您的答案标记为正确,因为显然没有办法,并且我用 xmlattributesoverrides 序列化了 c# 对象本身并将其插入到肥皂体中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 2018-04-12
    • 2017-11-24
    相关资源
    最近更新 更多