从XML创建对象的自动映射程序
本文关键字:映射程序 XML 创建对象 | 更新日期: 2023-09-27 18:00:55
如果我有以下类:
class SPUser
{
public int ID { get; set; }
public string Name { get; set; }
public string LoginName { get; set; }
public string Email { get; set; }
public bool IsSiteAdmin { get; set; }
public bool IsSiteAuditor { get; set; }
public bool IsDomainGroup { get; set; }
public List<SPGroup> Groups { get; set; }
}
我使用的是sharepointweb服务,它为类中的每个属性返回一个带有属性的XML,例如:
<Users>
<User Name="name" Description="desc" ..... />
</Users>
是否有任何方法可以使用AutoMapper将XML片段映射到SPUser
类实例?
博客已被删除-这是@DannyDouglass 的帖子的Bing档案
使用AutoMapper和Linq简化Xml数据对Xml 的使用
我最近在工作中遇到了一个需要手动使用几个SOAP web服务的场景,我相信您可以想象这相当单调。我和一位同事(Seth Carney)尝试了几种不同的方法,但我们最终确定了一个解决方案,该解决方案简化了xml的使用,并最终使代码更易于测试。该解决方案的核心是利用AutoMapper,一种开源对象对象映射工具,在XElements之间创建链接(http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.aspx)在我们创建的SOAP消息和自定义合约中返回–以可重用的方式。
我制作了一个快速演示,展示了如何使用相同的方法来消费和显示Twitter公共时间线(http://api.twitter.com/1/statuses/public_timeline.xml)(使用API的Xml响应类型)。
注意:以下示例的源代码可以在我的GitHub页面上找到:https://github.com/DannyDouglass/AutoMapperXmlMappingDemo
- 获取项目设置
在创建了一个基本的MVC3(下载测试版)项目和相关的测试项目后,第一步是安装AutoMapper包。我一直在使用微软最近发布的包管理系统NuGet来安装任何开源依赖项。以下命令是在我的MVC3项目中设置AutoMapper所需的全部内容(点击此处阅读更多关于NuGet的信息(http://weblogs.asp.net/scottgu/archive/2010/10/06/announcing-nupack-asp-net-mvc-3-beta-and-webmatrix-beta-2.aspx)在这里(http://weblogs.asp.net/scottgu/archive/2010/10/06/announcing-nupack-asp-net-mvc-3-beta-and-webmatrix-beta-2.aspx)):
PM> add-package AutoMapper
- 创建映射
安装了AutoMapper之后,我就可以开始创建xml到对象映射所需的组件了。第一步是创建一个在我的应用程序中用于表示Tweet对象的快速合约:
public interface ITweetContract
{
ulong Id { get; set; }
string Name { get; set; }
string UserName { get; set; }
string Body { get; set; }
string ProfileImageUrl { get; set; }
string Created { get; set; }
}
这里没有什么疯狂的,只是一个简单的实体。这些都是Twitter API响应中提供的字段,对某些字段使用不同的名称。在源对象和目标对象具有相同名称的简单情况下,您可以使用以下语法快速设置地图:
Mapper.CreateMap<SourceObj, DestinationObj>();
但是,AutoMapper默认情况下不支持Xml。我必须指定要映射的字段。使用AutoMapper中的Fluent API,我可以链接我的字段映射。看看我的例子中映射的一个示例字段——推文的正文:
Mapper.CreateMap<XElement, ITweetContract>()
.ForMember(
dest => dest.Body,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("text")))
一开始可能看起来很复杂,但这里真正发生的是,我们正在向AutoMapper提供关于在源对象中使用什么值以及如何将其映射到目标对象的属性的详细信息。在上面的身体场映射中,我想重点关注一条特定的线:
options => options.ResolveUsing<XElementResolver<ulong>>()
.FromMember(source => source.Element("id")))
XElementResolver是一个自定义值解析程序(http://automapper.codeplex.com/wikipage?title=Custom%20Value%20Resolvers)Seth用来处理解析XmlElement源对象以检索在映射中使用的强类型值。我稍后会详细介绍,但在我们继续之前,请看一下我的完整地图:
Mapper.CreateMap<XElement, ITweetContract>()
.ForMember(
dest => dest.Id,
options => options.ResolveUsing<XElementResolver<ulong>>()
.FromMember(source => source.Element("id")))
.ForMember(
dest => dest.Name,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("user")
.Descendants("name").Single()))
.ForMember(
dest => dest.UserName,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("user")
.Descendants("screen_name").Single()))
.ForMember(
dest => dest.Body,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("text")))
.ForMember(
dest => dest.ProfileImageUrl,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("user")
.Descendants("profile_image_url").Single()))
.ForMember(
dest => dest.Created,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("created_at")));
- 通用XElementResolver
这个自定义值解析器是允许这些XElement-to-Contract映射在原始解决方案中工作的真正密钥。正如我们在上面看到的,我在这个例子中重用了这个解析器。这就是创建自定义解析器类所必需的全部内容:
public class XElementResolver<T> : ValueResolver<XElement, T>
{
protected override T ResolveCore(XElement source)
{
if (source == null || string.IsNullOrEmpty(source.Value))
return default(T);
return (T)Convert.ChangeType(source.Value, typeof(T));
}
}
这个通用XElementResolver允许使用它轻松地传递在上面的映射中检索到的值的类型。例如,以下语法用于在上面Id字段的.ForMember()声明中强键入从XmlElement检索到的值:
ResolveUsing<XElementResolver<ulong>>()
在我的映射完全配置和实例化后,我准备调用Twitterneneneba API并利用AutoMapper来显示最新的公共时间线。
- 把碎片拼在一起
我创建了一个简单的类,负责检索Twitter API响应:
public class TwitterTimelineRetriever
{
private readonly XDocument _twitterTimelineXml;
public TwitterTimelineRetriever()
{
_twitterTimelineXml = XDocument
.Load("http://api.twitter.com/1/statuses/public_timeline.xml");
}
public IEnumerable<ITweetContract> GetPublicTimeline(int numberOfTweets)
{
var tweets = _twitterTimelineXml.Descendants("status")
.Take(numberOfTweets);
return tweets.Select(Mapper.Map<XElement, ITweetContract>).ToList();
}
}
GetPublicTimeline方法是一个简单的方法,你猜对了,通过利用我们之前创建的地图来返回Twitter公共时间线:
return tweets.Select(Mapper.Map<XElement, ITweetContract>).ToList();
在我的MVC3站点的HomeController中,我可以快速调用检索方法,请求最后10个结果:
public class HomeController : Controller
{
private TwitterTimelineRetriever _twitterTimelineRetriever;
public ActionResult Index()
{
_twitterTimelineRetriever = new TwitterTimelineRetriever();
ViewModel.Message = "Twitter Public Timeline";
return View(_twitterTimelineRetriever.GetPublicTimeline(10));
}
}
最后,在使用微软新的Razor视图引擎对我的视图进行了一点格式化之后,我的公共时间线就显示出来了!
您需要在.NET中检查XML序列化,这是将对象序列化为XML或从XML反序列化的方法。
Automapper可以用来设置两个对象之间的属性——它根本不处理XML。
更多资源:
- C#教程-XML序列化
虽然有人使用AutoMapper将XML映射到POCO,而不是使用XMLSerialization。我发现以下博客条目:-使用AutoMapper和Linq简化Xml数据对Xml 的使用
如果示例还不够,那么这足以让您开始实现自己的通用自定义解析器。
EDIT:固定链接EDIT:真正固定的链接
您可以为此目的使用XML反序列化。在.NET中,我们有XmlSerializer和DataContractSerializer