Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
- using System.Web.Mvc;
- using System.Xml;
- using System.Xml.Linq;
- /// <summary>
- /// A value provider for XML data
- /// </summary>
- /// <remarks>
- /// Originally from http://www.nogginbox.co.uk/Media/files/XmlValueProviderFactory.txt
- /// Changed to add XML attribute parsing to object properties and array support to
- /// match how I serialize arrays.
- /// </remarks>
- public class XmlValueProviderFactory : ValueProviderFactory
- {
- /// <summary>
- /// Returns a value-provider object for the specified controller context.
- /// </summary>
- /// <param name="controllerContext">An object that encapsulates information about the current HTTP request.</param>
- /// <returns>A value-provider object.</returns>
- public override IValueProvider GetValueProvider(ControllerContext controllerContext)
- {
- XDocument xmlData = GetDeserializedXml(controllerContext);
- if (xmlData == null)
- {
- return null;
- }
- Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
- AddToBackingStore(backingStore, string.Empty, xmlData.Root);
- return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
- }
- /// <summary>
- /// Adds to the xml document to the backing store.
- /// </summary>
- /// <param name="backingStore">The backing store.</param>
- /// <param name="prefix">The XML prefix.</param>
- /// <param name="xmlContainer">The XML container to add.</param>
- private static void AddToBackingStore(IDictionary<string, object> backingStore, string prefix, XContainer xmlContainer)
- {
- if (xmlContainer.NodeType == XmlNodeType.Element)
- {
- var element = (XElement)xmlContainer;
- // Look for XML attributes first, to map them to C# properties
- // Attribute names in XML elements must be unique, so we don't need to faff around with checking for arrays.
- foreach (var attribute in element.Attributes())
- {
- backingStore.Add(MakePropertyKey(prefix, attribute.Name.LocalName), attribute.Value);
- }
- // Now look for child elements.
- var arrayElements = new List<string>();
- foreach (var child in element.Elements())
- {
- if (child.HasAttributes)
- {
- // Nested container
- AddToBackingStore(backingStore, element.Name.LocalName, child);
- }
- else if (child.HasElements)
- {
- // Potential array
- AddToBackingStore(backingStore, element.Name.LocalName, child);
- }
- else
- {
- if (arrayElements.Contains(element.Name.LocalName))
- {
- // Do nothing, it's another occurrence of an array item, and we've looped through them all.
- continue;
- }
- // Check for multiple elements of the same name
- if (element.Elements(child.Name.LocalName).Count() > 1)
- {
- // We have an element which occurs multiple times that we've not seen yet.
- arrayElements.Add(element.Name.LocalName);
- int offset = 0;
- foreach (var arrayItem in element.Elements(child.Name.LocalName))
- {
- backingStore.Add(MakeArrayKey(prefix, element.Name.LocalName, offset++), arrayItem.Value);
- }
- }
- else
- {
- backingStore.Add(MakePropertyKey(prefix, child.Name.LocalName), child.Value);
- }
- }
- }
- }
- }
- /// <summary>
- /// Gets the XML from the request's input stream.
- /// </summary>
- /// <param name="controllerContext">The context containing the request</param>
- /// <returns>An XDocument containing the request contents.</returns>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Error handling.")]
- private static XDocument GetDeserializedXml(ControllerContext controllerContext)
- {
- var contentType = controllerContext.HttpContext.Request.ContentType;
- if (!contentType.StartsWith("text/xml", StringComparison.OrdinalIgnoreCase) &&
- !contentType.StartsWith("application/xml", StringComparison.OrdinalIgnoreCase))
- {
- // not an XML request
- return null;
- }
- XDocument xml;
- try
- {
- var xmlReader = new XmlTextReader(controllerContext.HttpContext.Request.InputStream);
- xml = XDocument.Load(xmlReader);
- }
- catch (Exception)
- {
- return null;
- }
- return xml.FirstNode == null ? null : xml;
- }
- /// <summary>
- /// Makes a key from the combination of the array name and index position.
- /// </summary>
- /// <param name="prefix">The property prefix.</param>
- /// <param name="propertyName">The property name.</param>
- /// <param name="index">The index position within the array.</param>
- /// <returns>A key from the combination of the prefix, array name and index position.</returns>
- private static string MakeArrayKey(string prefix, string propertyName, int index)
- {
- // Now we need to eat the prefix a little.
- // If for example we had <customer><address><line>1</line><line2>2</line></address></customer> we have a problem
- // The item key should be address[0], address[1] etc. But by the time this gets call the prefix has already has
- // the surrounding element. We need to hop up.
- prefix = prefix.Contains('.') ? prefix.Substring(0, prefix.LastIndexOf(".", StringComparison.OrdinalIgnoreCase) - 1) : string.Empty;
- return MakePropertyKey(prefix, propertyName) + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
- }
- /// <summary>
- /// Makes a key from the combination of the prefix and the property name.
- /// </summary>
- /// <param name="prefix">The property prefix.</param>
- /// <param name="propertyName">The property name.</param>
- /// <returns>A key from the combination of the prefix and the property name.</returns>
- private static string MakePropertyKey(string prefix, string propertyName)
- {
- return string.IsNullOrEmpty(prefix) ? propertyName : prefix + "." + propertyName;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement