c# - Automapper 从 XML 创建对象

标签 c# sharepoint automapper linq-to-xml

如果我有以下类(class):

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

我正在使用 sharepoint Web 服务,它返回一个 XML,其中包含我类中每个属性的属性,例如:
<Users>
    <User Name="name" Description="desc" ..... />
</Users>

有没有办法使用 AutoMapper 将 XML 片段映射到 SPUser类实例?

最佳答案

博客已被删除 - 这是@DannyDouglass 帖子的 Bing 存档

使用 AutoMapper 和 Linq-to-Xml 简化 Xml 数据的使用

我最近在工作中遇到了一个需要手动使用多个 SOAP Web 服务的场景,我相信您可以想象这种场景相当单调。我和一位同事 (Seth Carney) 尝试了几种不同的方法,但我们最终确定了一个解决方案,该解决方案简化了 xml 的使用并最终使代码更具可测试性。该解决方案以利用 AutoMapper(一种开源对象-对象映射工具)为中心,以可重用的方式在 SOAP 消息中返回的 XElements(http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.aspx) 与我们创建的自定义契约(Contract)之间创建链接。

我整理了一个快速演示,展示了如何使用相同的方法来使用和显示 Twitter 公共(public)时间轴 (http://api.twitter.com/1/statuses/public_timeline.xml)(使用 API 的 Xml 响应类型)。

注意:以下示例的源代码可以在我的 GitHub 页面上找到:https://github.com/DannyDouglass/AutoMapperXmlMappingDemo

  • 获取项目设置

  • 创建基本的 MVC3(下载测试版)项目和相关的测试项目后,第一步是安装 AutoMapper 包。我一直在使用 Microsoft 最近发布的包管理系统 NuGet 来安装任何开源依赖项。以下命令是在我的 MVC3 项目中设置 AutoMapper 所需的全部内容(在此处(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)阅读有关 NuGet 的更多信息):
    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 提供有关在我的源对象中使用什么值以及如何将其映射到目标对象的属性的详细信息。在上述 Body 字段映射中,我想重点关注一行:
    options => options.ResolveUsing<XElementResolver<ulong>>()
                        .FromMember(source => source.Element("id")))  
    

    XElementResolver 是一个自定义值解析器 ( http://automapper.codeplex.com/wikipage?title=Custom%20Value%20Resolvers ),赛斯想出它来处理解析 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>>()
    

    完全配置和实例化我的映射后,我准备调用 Twitter API 并利用 AutoMapper 来显示最新的公共(public)时间线。
  • 将碎片拼凑在一起

  • 我创建了一个负责检索 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 方法是一个简单的方法,您猜对了,通过利用我们之前创建的 map 返回 Twitter 公共(public)时间线:
    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));
    }
    }
    

    最后,在使用 Microsoft 的新 Razor View 引擎对我的 View 进行一些格式化之后,我的公共(public)时间线显示出来了!

    关于c# - Automapper 从 XML 创建对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6543659/

    相关文章:

    c# - 使用 lambda 表达式与私有(private)方法

    sharepoint - 为什么在使用 SharePoint REST API 处理列表项时内部字段名称不匹配?

    c# - MVC 从远距离相关的多个域模型创建 View 模型

    automapper - 必须解析为顶级成员

    c# - 我在 SQL Server 复制项目中收到 "An attempt was made to load a program with an incorrect format"错误

    c# - 由于异常 : "Access denied for user ' root' @'localhost' (using password: YES)",无法从 C# 应用程序访问 MySQL

    sharepoint - 按修改日期从 Sharepoint 复制文件夹

    自动映射器 : partial mapping

    c# - 从数据库创建对象列表

    sharepoint - SharePoint 列表可以有多少个事件接收器?