Monday, 24 March 2014

XML Validation against a schema in C#

There are two ways to validate an incoming an XML message against a schema:

1. Preload the schemas into the validator
2. Resolve and load the resources during processing

Doing both is incompatible and you will end up with message "The global element 'TopElementName' has already been declared".

Method 1

The schemas are loaded into the XmlReaderSettings.

public void Validate(string xml)
        {
            // Thread.CurrentThread.ManagedThreadId.Dump();
            ValidationEvents = new List();

            // Get the embedded resource from the DLL
            var resourceAssembly = this.GetType().Assembly;
            var schemas = LoadSchemasFromEmbeddedResource(resourceAssembly, new Dictionary() 
         { 
          { "http://www.w3.org/2000/09/xmldsig#", "NewBusiness.Domain.Schema_Resources.xmldsig-core-schema.xsd" },   // XmlSchemaValidationException is thrown without inclusion
                { "http://www.origostandards.com/schema/soap/v1", "NewBusiness.Domain.Schema_Resources.SOAPMessageControl.xsd" },
                { "http://www.origostandards.com/schema/ProcessSIPPIDPRApplication/v1.0/Common/CreateRequest", "NewBusiness.Domain.Schema_Resources.ProcessSIPPIDPRApplicationCommonCreateRequest.xsd" },
                { "http://www.origostandards.com/schema/ProcessSIPPIDPRApplication/v1.0/CreateRequest", "NewBusiness.Domain.Schema_Resources.ProcessSIPPIDPRApplicationCreateRequest.xsd" }
         });

            // Set the validation settings.
            var settings = new XmlReaderSettings();
            settings.Schemas = schemas;
            settings.ValidationType = ValidationType.Schema;
            settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
            // settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;      // Allows the processor to load the schema from the user-defined location
            // settings.XmlResolver = new XmlResourceResolver();                                   // If ProcessSchemaLocation is on then the resolver searches for resources in the assembly
            settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
            settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
            settings.DtdProcessing = DtdProcessing.Ignore;

            // Create the XmlReader object.
            try
            {
                using (var textReader = new StringReader(xml))
                {
                    using (var reader = XmlReader.Create(textReader, settings))
                    {
                        while (reader.Read()) ;
                    }
                }
            }
            catch (XmlSchemaValidationException schemaException)
            {
                ValidationEvents.Add(new ValidationEvent() { Severity = XmlSeverityType.Error, Message = "Schema load error: " + schemaException.Message });
            }
        }

private XmlSchemaSet LoadSchemasFromEmbeddedResource(Assembly assembly, Dictionary namespacePaths)
        {
            var schemaSet = new XmlSchemaSet();
            var readerSettings = new XmlReaderSettings 
            { 
                XmlResolver = new XmlResourceResolver(), 
                ValidationType = ValidationType.None,
                DtdProcessing = DtdProcessing.Ignore,
            };
            readerSettings.ValidationEventHandler += new ValidationEventHandler(SchemaLoadCallBack);

            foreach (var entry in namespacePaths)
            {
                using (var stream = assembly.GetManifestResourceStream(entry.Value))
                {
                    // stream.Seek(0, SeekOrigin.Begin);

                    var reader = XmlReader.Create(stream, readerSettings);
                    schemaSet.Add(entry.Key, reader);
                }
            }

            return schemaSet;
        }

Method 2 involves the following two lines of code:

settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;      // Allows the processor to load the schema from the user-defined location
settings.XmlResolver = new XmlResourceResolver();                                   // If ProcessSchemaLocation is on then the resolver searches for resources in the assembly

The ProcessSchemaLocation means that the reader will process a schema location that is specified in the XML (shown in bold):

<Message xsi:schemaLocation="http://www.origostandards.com/schema/ProcessSIPPIDPRApplication/v1.0/CreateRequest ProcessSIPPIDPRApplicationCreateRequest.xsd">
</Message>

This allows the schema location to be specified in the incoming XML and the reader will try and load it.
The code below shows a custom XML resolver that will attempt to load the schema from an embedded resource included within the current assembly.

public class XmlResourceResolver : XmlResolver
    {
        public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
        {
            var settings = new XmlReaderSettings();
            settings.ValidationType = ValidationType.None;

            if (absoluteUri.Segments.Length > 0)
            {
                var filename = absoluteUri.Segments[absoluteUri.Segments.Length - 1];

                var assembly = this.GetType().Assembly;
                var stream = assembly.GetManifestResourceStream("NewBusiness.Domain.Schema_Resources." + filename);
                return stream;
            }

            return null;
        }

        private void ValidationEventHandler(object sender, ValidationEventArgs e)
        {
        }


Tuesday, 11 March 2014

HTML: Padding underneath an IMG within a DIV

I had a simple bit of HTML with an image within a DIV. Yet despite setting padding and margin to 0, there would still be some padding displayed below the image.

<div>
    <img src="Logo.jpg" alt="Logo" />
</div>

The answer was to set

img { display:block; }
or

img { vertical-align:bottom; }


Since images are inline and text is inline, user agents leave some space for descender characters ( y, q, g ). To un-do this effect for images, you can specify either of the styles above, though you might want to make the rules more specific so you don't target an image you don't want this to apply on.

Another alternative as another poster has pointed out would be to set line-height to 0 on the parent element.

Thursday, 6 March 2014

Posting XML to the WebAPI passes a NULL input parameter

The default implementation of WebApi has a method that accepts a single value via the POST method:

public void PostData([FromBody]string value)
{
    Debug.WriteLine(value);
}

And this works fine with JSON. But what if you want to send a simple XML document?

<InputData><Data>Hello</Data></InputData>

If you pass this as application/xml the method is called, but the value comes through as null. There are various behaviours at work here. The serializer tries to serialize to classes. So really a POCO should be created like follows:

public class InputData
{
    public string Data { get; set; }
}

And the controller modified to take the class:

public void Post([FromBody]InputData xml)
{
    Debug.WriteLine(xml);
}

Yet still POSTing this to the Web API still returns null. The second behaviour is that by default the namespaces are expected. So only this works:
<InputData 
    xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://schemas.datacontract.org/2004/07/NewBusiness.WebApi.Controllers">
    <Data>Hello</Data>
</InputData>

Unless you allow the XML serializer in WebApiConfig:

config.Formatters.XmlFormatter.UseXmlSerializer = true;

And then this works:

<InputData><Data>Hello</Data></InputData>

To get it to serialize to a string, e.g.

public void PostData([FromBody]string value)
{
    Debug.WriteLine(value);
}

then you would need to change the serializer.