当发送回XML时,我必须在应用程序上工作。因为根始终是“ Reply”,但是属性不同,所以反序列化不能基于1个对象类型。
我现在编写了将Xml首先加载到新XmlDocument中的代码,读取了Name属性,并基于该属性,我尝试对其进行反序列化。在那儿 更好的方法?
我可以期望的xml示例:
<Reply Name="GetModulesList" Result="yes"><ModuleName="xxxxxx.exe" Path="\Debug\xxxxxxx.exe" Order="1"/></Reply>
<Reply Name="OpenRecipe" Result="yes"/>
您该如何解决?
XmlDocument doc = new XmlDocument();
doc.LoadXml(trimmedPart);
if (doc.DocumentElement?.Attributes != null)
{
XmlAttribute name = doc.DocumentElement.Attributes.Cast<XmlAttribute>().SingleOrDefault(a => String.Compare(a.Name, "name", StringComparison.OrdinalIgnoreCase) == 0);
replyName = name?.Value;
}
if (!string.IsNullOrEmpty(replyName))
{
XmlSerializer serializer = new XmlSerializer(GetSerializerObjectType(replyName));
using (StringReader reader = new StringReader(trimmedPart))
{
object obj = serializer.Deserialize(reader);
if (obj != null) returnList.Add(obj);
}
}
答案 0 :(得分:0)
我最近处理了类似的情况。这是一个非常粗略的概述,它是根据我的具体情况专门针对我的情况而改编的。
我的假设是,您将使用不同的类型来做不同的事情。如果是OpenRecipe
,您将做一件事;如果是GetModuleList
,您将做其他事情。
最大的区别是,我使用不同类型的每个“处理程序”都可能处理完全不同类型的数据-XML,JSON甚至Excel,因此处理程序以字节数组的形式接收内容并负责反序列化它。 (写内部版本的Biztalk并不是我的主意。)
在这种情况下,如果始终为XML,则可以
此类与反序列化Reply
(未知内部内容除外)有关,并将其传递给将以更强类型化的方式处理该内部内容的内容:
public class XmlReplyRouter
{
private readonly IReplyTypeMapper _replyTypeMapper;
private readonly IHandlerFactory _handlerFactory;
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(Reply));
public XmlReplyRouter(
IReplyTypeMapper replyTypeMapper,
IHandlerFactory handlerFactory)
{
_replyTypeMapper = replyTypeMapper;
_handlerFactory = handlerFactory;
}
public void RouteReply(string replyXml)
{
using (var reader = new StringReader(replyXml))
{
var reply = (Reply)_serializer.Deserialize(reader);
var replyType = _replyTypeMapper.GetReplyType(reply.Name);
var handler = _handlerFactory.GetHandler(replyType);
handler.HandleReply(reply);
}
}
}
public class Reply
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Result { get; set; }
[XmlAnyElement]
public XmlNode InnerXml { get; set; }
public string XmlContent => InnerXml?.OuterXml;
}
这就是所有丑陋之处。
XmlAnyElement
属性允许我们反序列化而不知道该内部内容的内容。一旦知道要使用哪种类型,就可以分别反序列化。
IReplyTypeMapper
和IHandlerFactory
的实现可以是任何东西。就我而言,我不能使用DI容器,因此它包含一个Dictionary<string, IReplyHandler>
并根据名称选择了正确的容器。 (我使用的术语不同,但是概念相同。)
第一个确定类型(通过“名称”属性),第二个返回该类型的处理程序。
public interface IReplyTypeMapper
{
Type GetReplyType(string replyName);
}
public interface IHandlerFactory
{
IReplyHandler GetHandler(Type contentType);
}
最后,这是回复处理程序的接口和基类。
如您所见,基类实际上并没有做任何事情。它只是弥合object
与泛型类型之间的鸿沟,以便我们可以编写强类型的处理程序。
public interface IReplyHandler
{
void HandleReply(object content);
}
public abstract class BaseHandler<T> : IReplyHandler
{
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(T));
public void HandleReply(object content)
{
HandleReplyContent((T)content);
}
protected abstract void HandleReplyContent(T content);
}
如您所见,它与您所做的非常相似。不过,这样做的目的是,一旦我获得了最少的初始代码,其他所有东西都是像这样的强类型类:
public class SomeSpecificReplyHandler : BaseHandler<SomeSpecificReply>
{
// Maybe these have dependencies of their own. That's easiest if
// everything gets resolved from an IoC container.
protected override void HandleReplyContent(SomeSpecificReply content)
{
// do whatever
}
}
我不知道这是否对您的需求来说是过大的。我的意图是从一个数据几乎可以进入任何地方的入口点开始,然后迅速走到那里,所有代码都经过严格类型化且易于测试。所有这些类都是可测试的(我在键入时写了一些),各个处理程序也很容易测试。
This post包含有关在没有IoC容器的情况下实现工厂的其他一些详细信息。就我而言,我无法使用IoC容器,因此我不得不“手动”编写所有类。工厂的内部实现只是字典。这样,如果以后可以选择切换到IoC容器,则可以用DI注册代替整个内容。