Advertisement
blowdart

Updated XmlValueProviderFactory

Jul 17th, 2012
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 7.35 KB | None | 0 0
  1.     using System;
  2.     using System.Collections.Generic;
  3.     using System.Globalization;
  4.     using System.Linq;
  5.     using System.Web.Mvc;
  6.     using System.Xml;
  7.     using System.Xml.Linq;
  8.  
  9.     /// <summary>
  10.     /// A value provider for XML data
  11.     /// </summary>
  12.     /// <remarks>
  13.     /// Originally from http://www.nogginbox.co.uk/Media/files/XmlValueProviderFactory.txt
  14.     /// Changed to add XML attribute parsing to object properties and array support to
  15.     /// match how I serialize arrays.
  16.     /// </remarks>
  17.     public class XmlValueProviderFactory : ValueProviderFactory
  18.     {
  19.         /// <summary>
  20.         /// Returns a value-provider object for the specified controller context.
  21.         /// </summary>
  22.         /// <param name="controllerContext">An object that encapsulates information about the current HTTP request.</param>
  23.         /// <returns>A value-provider object.</returns>
  24.         public override IValueProvider GetValueProvider(ControllerContext controllerContext)
  25.         {
  26.             XDocument xmlData = GetDeserializedXml(controllerContext);
  27.             if (xmlData == null)
  28.             {
  29.                 return null;
  30.             }
  31.  
  32.             Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
  33.             AddToBackingStore(backingStore, string.Empty, xmlData.Root);
  34.             return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
  35.         }
  36.  
  37.         /// <summary>
  38.         /// Adds to the xml document to the backing store.
  39.         /// </summary>
  40.         /// <param name="backingStore">The backing store.</param>
  41.         /// <param name="prefix">The XML prefix.</param>
  42.         /// <param name="xmlContainer">The XML container to add.</param>
  43.         private static void AddToBackingStore(IDictionary<string, object> backingStore, string prefix, XContainer xmlContainer)
  44.         {
  45.             if (xmlContainer.NodeType == XmlNodeType.Element)
  46.             {
  47.                 var element = (XElement)xmlContainer;
  48.  
  49.                 // Look for XML attributes first, to map them to C# properties
  50.                 // Attribute names in XML elements must be unique, so we don't need to faff around with checking for arrays.
  51.                 foreach (var attribute in element.Attributes())
  52.                 {
  53.                     backingStore.Add(MakePropertyKey(prefix, attribute.Name.LocalName), attribute.Value);
  54.                 }
  55.  
  56.                 // Now look for child elements.
  57.                 var arrayElements = new List<string>();
  58.                 foreach (var child in element.Elements())
  59.                 {
  60.                     if (child.HasAttributes)
  61.                     {
  62.                         // Nested container
  63.                         AddToBackingStore(backingStore, element.Name.LocalName, child);
  64.                     }
  65.                     else if (child.HasElements)
  66.                     {
  67.                         // Potential array
  68.                         AddToBackingStore(backingStore, element.Name.LocalName, child);
  69.                     }
  70.                     else
  71.                     {
  72.                         if (arrayElements.Contains(element.Name.LocalName))
  73.                         {    
  74.                             // Do nothing, it's another occurrence of an array item, and we've looped through them all.
  75.                             continue;                            
  76.                         }
  77.  
  78.                         // Check for multiple elements of the same name
  79.                         if (element.Elements(child.Name.LocalName).Count() > 1)
  80.                         {
  81.                             // We have an element which occurs multiple times that we've not seen yet.
  82.                             arrayElements.Add(element.Name.LocalName);
  83.                             int offset = 0;
  84.  
  85.                             foreach (var arrayItem in element.Elements(child.Name.LocalName))
  86.                             {
  87.                                 backingStore.Add(MakeArrayKey(prefix, element.Name.LocalName, offset++), arrayItem.Value);
  88.                             }
  89.                         }
  90.                         else
  91.                         {
  92.                             backingStore.Add(MakePropertyKey(prefix, child.Name.LocalName), child.Value);
  93.                         }
  94.                     }
  95.                 }
  96.             }
  97.         }
  98.  
  99.         /// <summary>
  100.         /// Gets the XML from the request's input stream.
  101.         /// </summary>
  102.         /// <param name="controllerContext">The context containing the request</param>
  103.         /// <returns>An XDocument containing the request contents.</returns>
  104.         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Error handling.")]
  105.         private static XDocument GetDeserializedXml(ControllerContext controllerContext)
  106.         {
  107.             var contentType = controllerContext.HttpContext.Request.ContentType;
  108.             if (!contentType.StartsWith("text/xml", StringComparison.OrdinalIgnoreCase) &&
  109.                 !contentType.StartsWith("application/xml", StringComparison.OrdinalIgnoreCase))
  110.             {
  111.                 // not an XML request
  112.                 return null;
  113.             }
  114.  
  115.             XDocument xml;
  116.             try
  117.             {
  118.                 var xmlReader = new XmlTextReader(controllerContext.HttpContext.Request.InputStream);
  119.                 xml = XDocument.Load(xmlReader);
  120.             }
  121.             catch (Exception)
  122.             {
  123.                 return null;
  124.             }
  125.  
  126.             return xml.FirstNode == null ? null : xml;
  127.         }
  128.  
  129.         /// <summary>
  130.         /// Makes a key from the combination of the array name and index position.
  131.         /// </summary>
  132.         /// <param name="prefix">The property prefix.</param>
  133.         /// <param name="propertyName">The property name.</param>
  134.         /// <param name="index">The index position within  the array.</param>
  135.         /// <returns>A key from the combination of the prefix, array name and index position.</returns>
  136.         private static string MakeArrayKey(string prefix, string propertyName, int index)
  137.         {
  138.             // Now we need to eat the prefix a little.
  139.             // If for example we had <customer><address><line>1</line><line2>2</line></address></customer> we have a problem
  140.             // The item key should be address[0], address[1] etc. But by the time this gets call the prefix has already has
  141.             // the surrounding element. We need to hop up.
  142.             prefix = prefix.Contains('.') ? prefix.Substring(0, prefix.LastIndexOf(".", StringComparison.OrdinalIgnoreCase) - 1) : string.Empty;
  143.             return MakePropertyKey(prefix, propertyName) + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
  144.         }
  145.  
  146.         /// <summary>
  147.         /// Makes a key from the combination of the prefix and the property name.
  148.         /// </summary>
  149.         /// <param name="prefix">The property prefix.</param>
  150.         /// <param name="propertyName">The property name.</param>
  151.         /// <returns>A key from the combination of the prefix and the property name.</returns>
  152.         private static string MakePropertyKey(string prefix, string propertyName)
  153.         {
  154.             return string.IsNullOrEmpty(prefix) ? propertyName : prefix + "." + propertyName;
  155.         }
  156.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement