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?
If you have to do complex mapping behavior, it might be better to avoid using AutoMapper for that scenario. Reverse mapping can get very complicated very quickly, and unless it's very simple, you can have business logic showing up in mapping configuration.
AutoMapper is a popular object-to-object mapping library that can be used to map objects belonging to dissimilar types. As an example, you might need to map the DTOs (Data Transfer Objects) in your application to the model objects.
Inside this article, it discusses performance and it indicates that Automapper is 7 times slower than manual mapping. This test was done on 100,000 records and I must say I was shocked.
First install the NuGet Package Manager in your Visual Studio IDE. Once done, go to "Tools" -> "Library Packet Manager" -> "Packet manager Console". Press Enter. This will install AutoMapper and the next time you open MVC application in Visual Studio, it will automatically add a DLL reference to the project.
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
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
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")));
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With