A custom model binder for XML content

Most of the time the default model binder in MVC suffices and maps everything into your model cleanly. One thing that you can't do automatically is read XML into an XDocument. You could solve this with something along the lines of:

[ValidateInput(false)]
public ActionResult TransferXMLData(string xmlText, int? otherValue)
{
var document = XDocument.Parse(xmlText);

    return Json(new { Success = true, OtherValue = otherValue });
}

But this means that every action you want to use an XML parameter on will need this, along with any appropriate code for checking that the parameter isn't null, contains valid XML, etc,..

The solution I've used before is to create a custom model binder, which requires both the custom model binder and an attribute to use to decorate the arguments that should be transformed into an XDocument:

public class XDocumentModelBinderAttribute : CustomModelBinderAttribute
{
    /// <summary>
    /// Get a model binder that transforms an XML string into an XDocument
    /// </summary>
    public override IModelBinder GetBinder()
    {
        return XDocumentModelBinder.Instance;
    }
}
/// <summary>
/// A model binder that transforms the input into an XmlDocument
/// </summary>
public class XDocumentModelBinder : IModelBinder
{
    internal static XDocumentModelBinder Instance { get; } = new XDocumentModelBinder();

    object IModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // First check if request validation is required
        var shouldPerformRequestValidation = controllerContext.Controller.ValidateRequest && bindingContext.ModelMetadata.RequestValidationEnabled;
        var incomingData = bindingContext.GetValueFromValueProvider(shouldPerformRequestValidation).AttemptedValue;
        var document = XDocument.Parse(incomingData);
        return document;
    }
}
/// <summary>
/// Helper methods for XDocumentModelBinder
/// </summary>
public static class ModelBindingContextExtensions
{
    /// <remarks>
    /// Code originally from http://blogs.taiga.nl/martijn/2011/09/29/custom-model-binders-and-request-validation/
    /// </remarks>
    public static ValueProviderResult GetValueFromValueProvider(this ModelBindingContext bindingContext, bool performRequestValidation)
    {
        var unvalidatedValueProvider = bindingContext.ValueProvider as IUnvalidatedValueProvider;
        return (unvalidatedValueProvider != null)
                    ? unvalidatedValueProvider.GetValue(bindingContext.ModelName, !performRequestValidation)
                    : bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    }
}

A parameter on an action can then be decorated with the XDocumentModelBinderAttribute so that it gets transformed into an XDocument (along with ValidateInput to allow the XML through):

[ValidateInput(false)]
public ActionResult TransferXMLData([XDocumentModelBinder] XDocument xmlText, int? otherValue)
{
   var response = new
   {
        Success = true
   };
   return Json(response);
}

A working sample that uses this model binder can be found on GitHub at https://github.com/robertwray/BlogStuff.

About Rob

I've been interested in computing since the day my Dad purchased his first business PC (an Amstrad PC 1640 for anyone interested) which introduced me to MS-DOS batch programming and BASIC.

My skillset has matured somewhat since then, which you'll probably see from the posts here. You can read a bit more about me on the about page of the site, or check out some of the other posts on my areas of interest.

1 Comment

  • Gravatar Image

    It would be interesting to see if you could add the same kind of feature to Asp.Net Mvc Core 3 and also perhaps not only support XDocument but also strongly typed classes and so on. I will look into this soon. Thanks for the inspiration!

Add a Comment