c# - “System.Messaging”系统如何识别它发送和接收的对象的类型?
问题描述
编辑:问题的根本原因
我正在开发一个应用程序,该应用程序System.Messaging
通过XmlMessageFormatter
.
我想发送一个对象,比如说Class1
,有一个 ID 字段:
public class Class1{
public long Id1;
}
我还想发送另一个对象,比如说Class16
,有另一个 ID 字段:
public class Class16{
public long Id16;
}
在 XML 中,两者都需要如下所示:
<HM>Human_Message
<ID>Own_Identifier</ID>
</HM>
为了实现这一点,我正在使用以下[Xml]
类似配置:
第一类:
[XmlRoot(ElementName = "HM")]
public class Class1{
[XmlElement(ElementName = "ID")]
public long Id1;
}
第 16 类:
[XmlRoot(ElementName = "HM")]
public class Class16{
[XmlElement(ElementName = "ID")]
public long Id16;
}
如您所见,两个类的 XML 正文确实是相同的。
这甚至可能吗?
编辑:原始问题
我有一个基本类(简单类),从中继承了几个子类(大约 27 个)。
我正在使用标准 C#System.Messaging
系统来回发送对象。
非常简化:
发送方:
我有一个消息队列,正在做:
subClass1 Obj1 = subClass1(...);
...
Basic_Class Obj_To_Be_Sent = Basic_Class(Obj1);
System.Messaging.Message message = new System.Messaging.Message(Obj_To_Be_Sent);
obj_MessageQueue.Send(message, ...);
检查Obj_To_Be_Sent
时,类型正确。
发送后,当我查看Computer Management, Services and Applications, Message Queuing, ..., 时Properties,我看到了消息,但我无法验证类型是否仍然正确。
接收方:
我有一个_xmlMessageFormatter
, 包含(除其他外):
System.Type[] messageTypes = new System.Type[27];
messageTypes[0] = typeof(SubClass1);
...
messageTypes[15] = typeof(SubClass16);
...
message = this._receiveQueue.Receive();
Basic_Class general = (Basic_Class)this._xmlMessageFormatter.Read(message);
Type objectType= general.GetType();
令我惊讶的是,objectType
是错误的(据信是SubClass16
)。
此应用程序以前运行良好,但现在似乎有些失败。我遇到的最大问题是我不知道如何检查发送消息和获取接收消息类型之间的步骤。
有没有人知道Computer Management, Services and Applications, Message Queuing, ...,我如何检查发送方的对象类型是否正常?
有人知道_xmlFormatter.Read()
andGetType()
吗?(已经在 之后Read()
,监视窗口提到了general
错误的类型)
提前致谢
更多调查后编辑
我删除了我自己的答案,因为问题没有完全解决。
同时我发现[XmlRoot]
条目导致了上述问题:我一直在[XmlRoot]
为不同的类使用相同的条目。
有办法区分吗?
供您参考,我已经尝试了以下方法,但没有奏效:
第一类:
[XmlRoot(ElementName = "HM", DataType = "subClass1", Namespace="Namespace")]
public class subClass1 : Basic_Class
类2:
[XmlRoot(ElementName = "HM", DataType = "subClass16", Namespace="Namespace")]
public class subClass16 : Basic_Class
而_xmlFormatter.TargetTypes
包含的条目如下:
Name = "subClass1" FullName="Namespace.Class1"
Name = "subClass16" FullName="Namespace.Class16"
有人有什么想法吗?
解决方案
TL/DR
XmlMessageFormatter
通过检查 XML根元素名称和命名空间 URI 并在提供的 中找到兼容的类型来识别接收到的对象类型TargetTypes
:
指定将由格式化程序从提供的消息中反序列化的一组可能类型。
因此,您需要XmlRootAttribute.ElementName
为每个派生类型指定不同的根元素名称,而不是"HM"
为所有类型使用相同的名称。完成此操作后,您应该能够在XmlMessageFormatter
不丢失类型信息的情况下发送和接收它们。
如果由于某种原因无法IMessageFormatter
做到这一点,并且您需要在所有类上使用相同的根名称“HM”,您将需要基于XmlMessageFormatter
通过其他机制(例如xsi:type
属性)对类型进行编码来实现自己的自定义。
解释
您XmlMessageFormatter
用于发送和接收格式化为 XML 的多态类型层次结构的实例。在内部,此序列化程序用于XmlSerializer
对 XML 进行序列化和反序列化。这个序列化器支持两种不同的机制来交换多态类型:
可以通过对每种类型使用不同的(根)元素名称来指定类型信息。
由于根元素名称默认由类名给出,您可能不需要通过元数据指示不同的根元素名称,但如果这样做,请使用
XmlRootAttribute.ElementName
:在 XML 文档实例中生成和识别的 XML 根元素的名称。默认是序列化类的名称。
如果您选择这种机制,您的类将类似于:
[XmlRoot(ElementName = "Basic_Class", Namespace="Namespace")] public class Basic_Class { } [XmlRoot(ElementName = "Class1", Namespace="Namespace")] public class Class1 : Basic_Class { } [XmlRoot(ElementName = "Class16", Namespace="Namespace")] public class Class16 : Basic_Class { }
使用此机制时,
XmlSerializer
为要序列化的具体类型构造一个。如果为所有子类型发出一个公共根元素,则可以通过
xsi:type
属性指定类型信息。该属性是 的缩写
{http://www.w3.org/2001/XMLSchema-instance}type
,是一个w3c 标准属性,它允许元素显式声明其类型,例如,当它是预期元素类型的多态子类型时。XmlSerializer
支持此属性并将使用它来确定要为这种多态类型反序列化的对象的实际类型。如果您选择这种机制,您的类将类似于:
[XmlRoot(ElementName = "Basic_Class", Namespace="Namespace")] [XmlInclude(typeof(Class1)), XmlInclude(typeof(Class16))] // Include all subtypes here! public class Basic_Class { } [XmlRoot(ElementName = "Class1", Namespace="Namespace")] public class Class1 : Basic_Class { } [XmlRoot(ElementName = "Class16", Namespace="Namespace")] public class Class16 : Basic_Class { }
使用此机制时,为共享基类型
Basic_Class
而不是具体类型构造一个序列化程序。
但是,在这两种机制中,只有第一种由 支持,从参考源XmlMessageFormatter
可以看出。简单地为传入对象的具体类型构造或使用默认序列化器:Write(Message message, object obj)
Type serializedType = obj.GetType(); XmlSerializer serializer = null; if (this.targetSerializerTable.ContainsKey(serializedType)) serializer = (XmlSerializer)this.targetSerializerTable[serializedType]; else { serializer = new XmlSerializer(serializedType); this.targetSerializerTable[serializedType] = serializer; } serializer.Serialize(stream, obj);
由于序列化程序是使用 构造的obj.GetType()
,因此根元素名称将是为派生的具体类指定的名称,并且xsi:type
不会包含信息。
该Read(Message message)
方法循环遍历为 构造的默认序列化程序,TargetTypes
并使用第一个XmlSerializer.CanDeserialize(XmlReader)
返回的序列化程序true
:
foreach (XmlSerializer serializer in targetSerializerTable.Values) { if (serializer.CanDeserialize(reader)) return serializer.Deserialize(reader); }
该方法反过来只是检查根元素名称和命名空间是否与要反序列化的类型兼容。这就是为什么您需要为每种具体类型使用不同的根元素名称。
实现自己的IMessageFormatter
.
如上所述,XmlMessageFormatter
仅通过不同的根元素名称支持多态性。如果这是不可接受的,您将需要IMessageFormatter
通过其他一些机制(例如上述xsi:type
属性)来实现对类型进行编码的您。
例如,以下IMessageFormatter
支持通过提供的序列化和反序列化XmlSerializer
:
public class OverrideXmlMessageFormatter : IMessageFormatter
{
readonly XmlSerializer serializer;
public OverrideXmlMessageFormatter(XmlSerializer serializer)
{
if (serializer == null)
throw new ArgumentNullException();
this.serializer = serializer;
}
#region IMessageFormatter Members
public bool CanRead(Message message)
{
if (message == null)
throw new ArgumentNullException();
var stream = message.BodyStream;
bool canRead;
try
{
using (var reader = XmlReader.Create(message.BodyStream))
canRead = serializer.CanDeserialize(reader);
}
catch (Exception)
{
canRead = false;
}
message.BodyStream.Position = 0; // reset stream in case CanRead is followed by Deserialize
return canRead;
}
public object Read(Message message)
{
if (message == null)
throw new ArgumentNullException();
using (var reader = XmlReader.Create(message.BodyStream))
return serializer.Deserialize(reader);
}
public void Write(Message message, object obj)
{
if (message == null || obj == null)
throw new ArgumentNullException();
var stream = new MemoryStream();
serializer.Serialize(stream, obj);
message.BodyStream = stream;
message.BodyType = 0;
}
#endregion
#region ICloneable Members
public object Clone()
{
return new OverrideXmlMessageFormatter(serializer);
}
#endregion
}
然后发送和接收类型Class1
和/或Class16
的消息,定义如下XmlSerializerCache.MessagingSerializer
:
public static class XmlSerializerCache
{
static XmlSerializer CreateSharedSerializer(Type baseType, Type[] extraTypes, string rootName, string rootNamespace)
{
// baseType could be typeof(object) if there is no common base type.
return new XmlSerializer(baseType,
null,
extraTypes,
new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace },
null);
}
static XmlSerializer serializer = CreateSharedSerializer(
// The base type for the classes you want to send. Could be object if there is no more derived base type.
typeof(object),
// Add all the derived types of the classes you want to send
new[] { typeof(Class1), typeof(Class16) },
// Your required root element name.
"MH",
// Your required root element namespace.
"");
// The serializer must be statically cached to avoid a severe memory leak, see https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
public static XmlSerializer MessagingSerializer { get { return serializer; } }
}
并设置
IMessageFormatter _xmlMessageFormatter = new OverrideXmlMessageFormatter(XmlSerializerCache.MessagingSerializer);
您将需要在发送方和接收方都使用此格式化程序。
发送的 XML 将如下所示(对于Class1
):
<MH xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xsi:type="Class1">
<ID>10101</ID>
</MH>
笔记
XmlRootAttribute.DataType
不用于指定多态类型信息。相反,它可用于指示元素包含某些特定 XML 原始类型的值,例如dateTime
,duration
等:XSD(XML 模式文档)数据类型。
因此,设置此值对您的应用程序没有帮助。
XmlSerializer
关于和多态性的一些相关问题包括:使用 with 属性覆盖序列化为 XML 时
XmlSerializer
,您必须静态缓存并重用序列化程序以避免严重的内存泄漏。有关原因,请参阅使用 StreamReader 和 XmlSerializer 的内存泄漏。如果您确实实现了自己的
IMessageFormatter
,您可以假设使用不同的序列化程序(例如 Json.NET)来实现自己的消息格式。
推荐阅读
- android - 当片段具有选项菜单时,Android Chrome Cast 介绍 Overlay 的行为不正确
- android - 如何在 Kotlin 中获取 Mac 地址
- python-3.x - Django 显示我的数据库中不存在表
- javascript - 没有`Worker-Loader`的Webpack中的Web Worker
- task - Asyncio 在另一个任务中启动一个任务?
- ruby-on-rails - 基于关联模型的 Rails 查询
- flutter - Flutter:检查 AppLifecycle 事件暂停
- unit-testing - 在 Corda 流的单元测试中抑制 oracle 调用
- r - 使用 bargraph.CI 分组条形图
- javascript - 什么、如何以及何时在自定义印迹上使用静态格式、格式和格式?