2011-07-01 12 views
7

Si tengo la clase siguiente:AutoMapper para crear objetos a partir de XML

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

y estoy usando los servicios web de SharePoint, que devuelven un XML con un atributo de cada establecimiento en mi clase, tales como:

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

¿Hay alguna forma de utilizar AutoMapper para asignar el fragmento XML a una instancia de clase SPUser?

Respuesta

8

blog ha sido eliminado - aquí está el archivo Bing del mensaje por @DannyDouglass

Simplificar el uso de XML de datos Con AutoMapper y LINQ to XML

Recientemente me encontré con un escenario en el trabajo que requiere el consumo manual de varios servicios web SOAP, que estoy seguro que se puede imaginar era bastante monótono. Un compañero de trabajo (Seth Carney) y yo probamos algunos enfoques diferentes, pero finalmente nos decidimos por una solución que simplificara el consumo del xml y finalmente hiciera que el código fuera más comprobable. Esa solución se centró en aprovechar AutoMapper, una herramienta de mapeo de objetos objetables de código abierto, para crear un enlace entre los elementos XE (http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.aspx) devueltos en los mensajes SOAP y los contratos personalizados que creamos, de forma reutilizable.

Armado una demostración rápida que muestra cómo podría usar el mismo enfoque para consumir y mostrar la línea de tiempo pública de Twitter (http://api.twitter.com/1/statuses/public_timeline.xml) (utilizando el tipo de respuesta Xml de la API).

Nota: El código fuente para el siguiente ejemplo se puede encontrar en mi página de GitHub: https://github.com/DannyDouglass/AutoMapperXmlMappingDemo

  1. Obtener la configuración del proyecto

Después de crear un MVC3 básica (descarga beta) del proyecto y el proyecto de prueba asociado, el primer paso fue instalar el paquete AutoMapper.He estado usando NuGet, el sistema de administración de paquetes recientemente anunciado de Microsoft, para instalar cualquier dependencia de código abierto. El siguiente comando era todo lo que se necesitaba para AutoMapper configuración en mi proyecto MVC3 (leer más sobre NuGet aquí (http://weblogs.asp.net/scottgu/archive/2010/10/06/announcing-nupack-asp-net-mvc-3-beta-and-webmatrix-beta-2.aspx) y aquí (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. creación de la correlación

Con AutoMapper instalado, estoy listo para comenzar a crear los componentes necesarios para la asignación de xml a objeto. El primer paso es crear un contrato rápida utilizado en mi solicitud para representar el objeto 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; } 
} 

Nada loco aquí - sólo una entidad simple. Estos son todos los campos que se proporcionan en la respuesta de la API de Twitter con un nombre diferente para algunos campos. En casos sencillos, donde los objetos de origen y de destino tienen el mismo nombre se puede configurar un mapa utilizando muy rápidamente esta sintaxis:

Mapper.CreateMap<SourceObj, DestinationObj>(); 

Sin embargo, AutoMapper no soporte XML por defecto que tengo que especificar los campos que estaré cartografía. Al usar la API Fluent en AutoMapper, puedo encadenar mis asignaciones de campo. Echar un vistazo a un campo ejemplo mapeado en mi ejemplo - Cuerpo del tweet:

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

Puede parecer complicado al principio, pero todo lo que realmente está sucediendo aquí es que estamos proporcionando detalles de AutoMapper en qué valor de usar en mi objeto fuente y cómo asignarlo a la propiedad del objeto de destino. Hay una línea en particular me gustaría centrarse en la anterior asignación de campos de cuerpo:

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

El XElementResolver es un resolvedor valor personalizado (http://automapper.codeplex.com/wikipage?title=Custom%20Value%20Resolvers) que Seth se le ocurrió para manejar el análisis del objeto de origen XmlElement para recuperar una valor fuertemente tipado para usar en el mapeo. Voy a detalle que más en un momento, pero antes de pasar echar un vistazo a mi total mapeo:

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. El XElementResolver Genérico

Esta costumbre el resolvedor de valores es la clave real que permitió que estos mapas XElement-to-Contract funcionen en la solución original. He reutilizado esta resolución en este ejemplo como vimos anteriormente. Esto era todo lo que era necesario para crear la clase resolución personalizada:

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

este XElementResolver genérica permite el uso de pasar fácilmente el tipo del valor recuperado en nuestro mapeo anteriormente. Por ejemplo, la siguiente sintaxis se utiliza para escribir fuertemente el valor recuperado del XmlElement en .ForMember del campo ID() declaración anterior:

ResolveUsing<XElementResolver<ulong>>() 

Con mi mapeo completamente configurado y instanciado, estoy listo para invocar el Twitter API y aproveche AutoMapper para mostrar esa última línea de tiempo pública.

  1. todo y las partes

he creado una clase simple responsable de la recuperación de la respuesta del API de Twitter:

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

El método GetPublicTimeline es un método sencillo volviendo, adivinó, la línea de tiempo pública de Twitter aprovechando el mapa que creamos anteriormente:

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

En HomeController de mi sitio MVC3 Puedo hacer una llamada rápida al método de recuperación, solicitando los últimos 10 resultados:

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

Y finalmente, después de un poco de formato en mi opinión utilizando el nuevo motor de vistas Razor de Microsoft, me tener mi calendario público mostrando!

+6

¿De qué manera el uso de AutoMapper es más "automático" que el uso manual de XML en el POCO? – user2864740

4

Al final del día, alguien ha usado AutoMapper para asignar XML a un POCO en lugar de ir por la ruta de XMLSerialization. Encontré la siguiente entrada en el blog: - Simplify Using Xml Data with AutoMapper and Linq-to-Xml

Esto es suficiente para comenzar a implementar un propio solucionador personalizado genérico si el ejemplo no es suficiente.

EDIT: enlace fijo EDIT: realmente fijo enlace

+0

ricardo - esa entrada de blog fue TAN útil. ¡¡Gracias!! –

+1

Ese enlace ahora está roto. Aquí uno de trabajo: http://dannydouglass.com/blog/2010/11/06/simplify-using-xml-data-with-automapper-and-linqtoxml/ – GFoley83

+1

@ GFoley83 ¡su enlace ahora también está roto! –

Cuestiones relacionadas