自动映射器从 XML 创建对象

发布于 2024-11-18 03:39:10 字数 630 浏览 2 评论 0原文

如果我有以下类:

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 类实例?

If I have the following 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; }
}

And I am using the sharepoint web services, which return an XML with an attribute for each property on my class, such as:

<Users>
    <User Name="name" Description="desc" ..... />
</Users>

Is there any way to use AutoMapper to map the XML fragment to an SPUser class instance?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

月隐月明月朦胧 2024-11-25 03:39:10

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

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

我最近在工作中遇到了一个需要手动使用多个 SOAP Web 服务的场景,我相信您可以想象当时相当单调。我和一位同事(Seth Carney)尝试了几种不同的方法,但我们最终确定了一种解决方案,可以简化 xml 的使用,并最终使代码更易于测试。该解决方案的核心是利用 AutoMapper(一种开源对象到对象映射工具)在 XElement 之间创建链接(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

  1. 获取项目设置

创建基本的 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
  1. 创建映射

安装了 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),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")));
  1. Generic 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 来显示最新的公共时间表。

  1. 将各个部分放在一起

我创建了一个简单的类,负责检索 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));
}
}

最后,使用 Microsoft 的新 Razor 视图引擎在我的视图中进行一些格式化后,我显示了我的公共时间线!

Blog has been deleted - here's the Bing archive of the post by @DannyDouglass

Simplify Using Xml Data With AutoMapper and Linq-to-Xml

I recently ran into a scenario at work that required manually consuming several SOAP web services, which I’m sure you can imagine was rather monotonous. A co-worker (Seth Carney) and I tried a few different approaches, but we finally settled on a solution that simplified consumption of the xml and ultimately made the code more testable. That solution centered around leveraging AutoMapper, an open source object-object mapping tool, to create a link between the XElements(http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.aspx) returned in the SOAP messages and custom contracts we created – in a reusable manner.

I put together a quick demo that shows how you could use the same approach to consume and display the Twitter Public Timeline (http://api.twitter.com/1/statuses/public_timeline.xml) (using the API’s Xml response type).

Note: The source code for the following example can be found on my GitHub page: https://github.com/DannyDouglass/AutoMapperXmlMappingDemo

  1. Getting the Project Setup

After creating a basic MVC3 (download beta) project and the associated test project, the first step was to get the AutoMapper package installed. I have been using NuGet, Microsoft’s recently announced package management system, to install any open source dependencies. The following command was all that was needed to setup AutoMapper in my MVC3 project (read more about NuGet here(http://weblogs.asp.net/scottgu/archive/2010/10/06/announcing-nupack-asp-net-mvc-3-beta-and-webmatrix-beta-2.aspx) and here(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
  1. Creating the Mapping

With AutoMapper installed I’m ready to get started creating the components necessary for the xml-to-object mapping. The first step is creating a quick contract used in my application to represent the Tweet object:

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

Nothing crazy here – just a simple entity. These are all fields that are provided in the response from the Twitter API using a different name for some fields. In simple cases where the source and destination objects have the same name you can setup a map very quickly using this syntax:

Mapper.CreateMap<SourceObj, DestinationObj>();

However, AutoMapper does not support Xml by default I have to specify the fields that I will be mapping. Using the Fluent API in AutoMapper I’m able to chain my field mappings. Take a look at one example field mapped in my example – the tweet’s Body:

Mapper.CreateMap<XElement, ITweetContract>()
.ForMember(
    dest => dest.Body,
    options => options.ResolveUsing<XElementResolver<string>>()
        .FromMember(source => source.Element("text")))

It may look complicated at first, but all that is really happening here is that we are providing details to AutoMapper on what value to use in my source object and how to map it to the destination object’s property. There is one particular line I would like to focus on in the above Body field mapping:

options => options.ResolveUsing<XElementResolver<ulong>>()
                    .FromMember(source => source.Element("id")))  

The XElementResolver is a custom value resolver (http://automapper.codeplex.com/wikipage?title=Custom%20Value%20Resolvers) that Seth came up with to handle parsing the XmlElement source object to retrieve a strongly-typed value for use in the mapping. I’ll detail that more in a moment, but before we move on take a look at my full mapping:

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")));
  1. The Generic XElementResolver

This custom value resolver is the real key that allowed these XElement-to-Contract maps to work in the original solution. I’ve reused this resolver in this example as we saw above. This was all that was necessary to create the custom resolver class:

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

This generic XElementResolver allows use to easily pass the type of the value retrieved in our mapping above. For example, the following syntax is used to strongly type the value retrieved from the XmlElement in the Id field’s .ForMember() declaration above:

ResolveUsing<XElementResolver<ulong>>()

With my mapping completely configured and instantiated, I’m ready to invoke the Twitter API and leverage AutoMapper to display that latest Public Timeline.

  1. Putting the Pieces Together

I created a simple class responsible for retrieving the Twitter API response:

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

The GetPublicTimeline method is a simple method returning, you guessed it, the Twitter Public Timeline by leveraging the map we created earlier:

return tweets.Select(Mapper.Map<XElement, ITweetContract>).ToList();

In my MVC3 site’s HomeController I can make a quick call to the retrieval method, requesting the last 10 results:

public class HomeController : Controller
{
private TwitterTimelineRetriever _twitterTimelineRetriever;  
public ActionResult Index()
{
    _twitterTimelineRetriever = new TwitterTimelineRetriever();  
    ViewModel.Message = "Twitter Public Timeline";  
    return View(_twitterTimelineRetriever.GetPublicTimeline(10));
}
}

And finally, after a little formatting in my View using the new Razor view engine from Microsoft, I have my public timeline displaying!

黯淡〆 2024-11-25 03:39:10

您需要查看 .NET 中的 XML 序列化 - 这就是将对象序列化为 XML 或从 XML 反序列化的方法。

Automapper 可用于设置两个对象之间的属性 - 它根本不处理 XML。

更多资源:

You need to check out XML serialization in .NET for this - that's the way to serialize an object to XML or deserialize it from XML.

Automapper can be used to set properties between two objects - it doesn't deal with XML at all.

More resources:

爱你是孤单的心事 2024-11-25 03:39:10

今天有点晚了,但有人使用 AutoMapper 将 XML 映射到 POCO,而不是沿着 XMLSerialization 路线。我发现以下博客条目:-
通过 AutoMapper 简化 Xml 数据的使用和 Linq-to-Xml

如果示例还不够,这足以让您开始实现自己的通用自定义解析器。

编辑:固定链接
编辑:真正固定的链接

Bit late in the day, but someone has used AutoMapper to map XML to a POCO rather than going down the XMLSerialization route. I found the following blog entry:-
Simplify Using Xml Data with AutoMapper and Linq-to-Xml

This is enough to get you started on implementing a generic custom resolver of your own if the example one is not enough.

EDIT: fixed link
EDIT: really fixed link

牵你的手,一向走下去 2024-11-25 03:39:10

您可以使用 XML 反序列化来实现此目的。在 .NET 中,我们有 XmlSerializerDataContractSerializer

You can user XML deserialization for this purposes. In .NET we have XmlSerializer and DataContractSerializer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文