Code Reference A collection of code for my reference (and perhaps other people too)

8Mar/100

Serialization to XML with CDATA tags

Sometimes you need CDATA tags around complex text in your destination XML.

There is no build in way of doing so (that I have found).

I found this nifty CdataWrapper class on Marc Gravell's blog. Thanks Marc!

Here is the basic class.

public sealed class CDataWrapper : IXmlSerializable
{
        // implicit to/from string
        public static implicit operator string(CDataWrapper value)
        {
            return value == null ? null : value.Value;
        }
        public static implicit operator CDataWrapper(string value)
        {
            return value == null ? null : new CDataWrapper
            {
                Value =
                    value
            };
        }
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }
        // "" => <Node/>
        // "Foo" => <Node><![CDATA[Foo]]></Node>
        public void WriteXml(XmlWriter writer)
        {
            if (!string.IsNullOrEmpty(Value))
            {
                writer.WriteCData(Value);
            }
        }
        // <Node/> => ""
        // <Node></Node> => ""
        // <Node>Foo</Node> => "Foo"
        // <Node><![CDATA[Foo]]></Node> => "Foo"
        public void ReadXml(XmlReader reader)
        {
            if (reader.IsEmptyElement)
            {
                Value = "";
            }
            else
            {
                reader.Read();
                switch (reader.NodeType)
                {
                    case XmlNodeType.EndElement:
                        Value = ""; // empty after all...
                        break;
                    case XmlNodeType.Text:
                    case XmlNodeType.CDATA:
                        Value = reader.ReadContentAsString();
                        break;
                    default:
                        throw new InvalidOperationException("Expected text/cdata");
                }
            }
        }
        // underlying value
        public string Value { get; set; }
        public override string ToString()
        {
            return Value;
        }
}

To use it, you must change your DataMember from String to CDataWrapper, make it private and give it a public property.

This:

[DataMember]
pubblic string Answer { get; set; }

Becomes this:

pubblic string Answer { get; set; }
[DataMember(Name="Answer", EmitDefaultValue=false)]
private CDataWrapper AnswerCDATA
{
    get { return Answer; }
    set { Answer = value; }
}

Then the serialization is done with one line:

.
.
.
XElement xml = Microsoft.ServiceModel.Web.SerializationExtensions.ToXml(object);
.

So simple!!!!

8Mar/100

More on Object Serialization to XML

It seems there are hundreds of ways to serialize object.

Here are just a few:

This one now seems very clumbsy. You pass in the object and the object type. It returns a string. I can't see the point of using this one any more now that I have found the next one.

public static string ContractObjectToXml<T>(T obj)
{
    DataContractSerializer dataContractSerializer = new DataContractSerializer(obj.GetType());
    String text;
    using (MemoryStream memoryStream = new MemoryStream())
    {
        dataContractSerializer.WriteObject(memoryStream, obj);
        byte[] data = new byte[memoryStream.Length];
        Array.Copy(memoryStream.GetBuffer(), data, data.Length);
        text = Encoding.UTF8.GetString(data);
    }
    return text;
}

This one I have started using is the most simple I have seen so far.

.
.
.
XElement xml = Microsoft.ServiceModel.Web.SerializationExtensions.ToXml(object);
.

This one returns a stream. It gives you a bit more control over what is shown and included in the XML.

public static Stream Serialize(Object obj)
{
            MemoryStream memoryStream = new MemoryStream();
            DataContractSerializer dcSerializer = new DataContractSerializer(obj.GetType());
            XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
            xmlWriterSettings.Encoding = new UTF8Encoding(false);
            xmlWriterSettings.ConformanceLevel = ConformanceLevel.Document;
            xmlWriterSettings.Indent = true;
            //xmlWriterSettings.OmitXmlDeclaration = true;

            using (XmlWriter xWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
            {
                dcSerializer.WriteObject(xWriter, obj);
                xWriter.Flush();
                //return Encoding.UTF8.GetString(memoryStream.ToArray());
                memoryStream.Position = 0;
                return memoryStream;
            }
}
21Dec/090

Secure WCF REST webservice – part 1 – Validate XML with Schema

I have spent a good amount of time workig to implement a good XML validation method. I followed may leads and ended with a simple solution.

My goals were:
* use WCF/REST
* Use a Schema to validate the incoming XML request
* Extend the deserialize method turn prohibit DTDs and to disable external entities
* Filter IPs based on a list in the Config file
* Use the newer WebServiceHost2 from the WCF REST Starter Kit

Problems I had:
* I tried extending IDispatchMessageInspector and IClientMessageInspector to add schema validation. This worked great on WS Services (wsHttpBinding), but I could not get it to work on WebServices (webHttpBinding). I could not find any exampels of working solution on the net on how to do this with web services. (here is the blog post on what I did come up with). I abandoned this approach.

* Schema validation is done on the xml before it is derialized or when it is deserialized to an object. Because I could not access the deserialization before accepting the incoming xml request, I was forced to have the incoming object type 'XElement' rather than the destination object.

My solution:
* I used the new WCF REST Starter Kit. This reduces the confit file to almost nothing. The endpoints are set automatically. All adjustments can be done thru attributes on the service method itself.

Here is my config file:

<?xml version="1.0"?>
<configuration>
  <appSettings>
     ...
  </appSettings>

  <system.web>
		<compilation debug="true"/>
  </system.web>

  <system.serviceModel>
		<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
  </system.serviceModel>
</configuration>

My Service:

// The following line sets the default namespace for DataContract serialized typed to be ""
[assembly: ContractNamespace("", ClrNamespace = "ThisIsMyService")]

namespace ThisIsMyService
{
    // TODO: Please set IncludeExceptionDetailInFaults to false in production environments
    [ServiceBehavior(IncludeExceptionDetailInFaults = true), AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed), ServiceContract]
    public partial class Service
    {
        [WebHelp(Comment = "This is my service")]
        [WebInvoke(UriTemplate = "MyService")]
        [OperationContract]
        public XElement MyService(XElement requestXml)
        {
            // Schema validation and deserialization are both done at the same time.
            // If there is a problem, null is returned and an exception is thrown with a HTTPStatusCode of Bad Request.
            var validatedObject = Helper.XmlToObject(requestXml, typeof(RequestObject),
                ConfigurationManager.AppSettings["SchemaPath"].ToString(),
                ConfigurationManager.AppSettings["SchemaNamespace"].ToString());

            // I do all the process and other work in the helper class to keep this clean.
            if (validatedObject is RequestObject)
                return new Helper().DoStuff((RequestObject)validatedObject);

            // If there is problem validating the incoming request XML, then the validated object will be null
            // Error validating request - return nothing
            return new XElement("root");
        }
    }
}

My service is very basic. All the logic is done in the helper class.

My helper class contains the static validation XMLtoObject method and the methods for doing the work of the service.

When you deserialize XML to an object, you can add a schema that will be used to check the XML.
What you don't easily find in the documentation, is the fact that the Schema must be read and validated before it can be added to the reader that reads and deserializes the incoming xml request.

public class EngineServiceHelper
{
    /// <summary>
    /// A schema is an XML file like all the rest.
    /// It also must be loaded and deserialized.
    /// To use a schema, it must be placed in a XmlSchemaSet.
    /// </summary>
    /// <param name="xml"></param>
    /// <param name="DestinationType"></param>
    /// <param name="schemaPath"></param>
    /// <param name="xmlNamespace"></param>
    /// <returns></returns>
    public static object XmlToObject(XElement xml, Type DestinationType, string schemaPath, string xmlNamespace)
    {
        ErrorMessage = string.Empty;
        ErrorsCount = 0;

        XmlSchemaSet schemaSet = new XmlSchemaSet();
        schemaSet.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler);
        schemaSet.Add(xmlNamespace, schemaPath);

        if (schemaSet.Count > 0)
        {
            try
            {
                // Set the setting for reading the Schema file
                XmlReaderSettings schemaSettings = new XmlReaderSettings();
                schemaSettings.ValidationType = ValidationType.Schema;
                schemaSettings.Schemas.Add(schemaSet);
                // To make the service more secure and prevent XML Bombs and such, disable DTDs
                xmlRequestSettings.ProhibitDtd = true;
                // Limit the size of the elements - 1024 = 1Kb
                schemaSettings.MaxCharactersFromEntities = 1024;
                // Adding the validation event handler prevents validation errors from throwing an exception.
                // We want to control when and how an exception is thrown.
                schemaSettings.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler);
                // create the reader for reading the schema
                XmlReader schemaReader = XmlReader.Create(xml.CreateReader(), schemaSettings);

                // Set the settings for reading the incoming XML request
                XmlReaderSettings xmlRequestSettings = new XmlReaderSettings();
                // To make the service more secure and prevent XML Bombs and such, disable DTDs
                xmlRequestSettings.ProhibitDtd = true;
                // Limit the size of the elements - 1024 = 1Kb
                schemaSettings.MaxCharactersFromEntities = 1024;
                // Create the reader for reading the XML request.
                XmlReader xmlReader = XmlReader.Create(schemaReader, xmlRequestSettings);

                // Deserialize the XML to an object
                XmlSerializer serializer = new XmlSerializer(DestinationType, xmlNamespace);
                object returnObject = serializer.Deserialize(xmlReader);

                // If there are errors, throw an exception
                if (ErrorsCount > 0)
                    throw new Exception(ErrorMessage);

                // If ther are no problems, return the deserialized object
                return returnObject;
            }
            catch (Exception ex)
            {
                // Using the WebProtocolException in the WCF REST Starter Kit, you can throw
                // and exception and choose the HTTPStatusCode that will be displayed to the end user.
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Your XML is invalid: " + ex.Message, null);
            }
        }
        return null;
    }

    /// <summary>
    /// Check the error severity and create the error message.
    /// Increment the error counter.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public static void settings_ValidationEventHandler(object sender, ValidationEventArgs e)
    {
        if (e.Severity == XmlSeverityType.Error)
        {
            ErrorMessage = "ERROR: " + ErrorMessage + e.Message + "\r\n";
            ErrorsCount++;
        }
        else
        {
            ErrorMessage = "WARNING: " + ErrorMessage + e.Message + "\r\n";
            ErrorsCount++;
        }
    }
}

The validation event handler allows you to catch the validation errors and handle them yourself.
I am checking the error severity and returning a message that reflects it.

9Oct/091

Object serialization for a WCF DataContact

The object you use for your WCF Data Contract should be simple. By default the only Attribute requried is '[DataMember]' on the properties and '[DataContract]' on the class.

These attributes define how the object will be serialized. The serialization is VERY important.

If your serialization is not correct, you will get unpredictable behavior. Some of the problems I experienced was values not being passed into the object from the xml string, exceptions thrown when the order is not correct or incorrect default values entered.

The most important items I found were
* order (alphabetical)
* root element attributes ()
* zero default for int values ('0')
* represent all possible values, even if they are not used ('' vs '' does not make a difference until it is an int/numeric/etc.)

Some of these are no-brainers. But the order? Why should the order matter when the DataMember attributes don't specify an order? It does and there seems to be no reason why.

Here is my object:

[DataContract]
public sealed class InputXMLObject
{
    // Required
    [DataMember(IsRequired = true)]
    public string Language { get; set; }
    [DataMember(IsRequired = true)]
    public string Name { get; set; }
    [DataMember(IsRequired = true)]
    public int MyId { get; set; }

    // Optional
    [DataMember(IsRequired = false, EmitDefaultValue = true)]
    public string SomeThing { get; set; }
    [DataMember(IsRequired = false, EmitDefaultValue = true)]
    public string SomethingElse { get; set; }
    [DataMember(IsRequired = false, EmitDefaultValue = true)]
    public string Timestamp { get; set; }
}

I serialized it with this nifty little method. I like it because it is generic. The most important part about it that it uses System.Runtime.Serialization.DataContractSerializer. I found this method on Peter Ritchie's blog.

public static string ContractObjectToXml<T>(T obj)
{
    DataContractSerializer dataContractSerializer = new DataContractSerializer(obj.GetType());
    String text;
    using (MemoryStream memoryStream = new MemoryStream())
    {
        dataContractSerializer.WriteObject(memoryStream, obj);
        byte[] data = new byte[memoryStream.Length];
        Array.Copy(memoryStream.GetBuffer(), data, data.Length);
        text = Encoding.UTF8.GetString(data);
    }
    return text;
}

After serialization, the object looked like this:

<InputXMLObject2 xmlns="http://schemas.datacontract.org/2004/07/MyService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Language>UK</Language><MyId>5</MyId><Name>Billy</Name><SomeThing i:nil="true"/><SomethingElse i:nil="true"/><Timestamp i:nil="true"/></InputXMLObject2>

I made it a bit more simple (the ' i:nil="true"' is not necessary):

<InputXMLObject xmlns="http://schemas.datacontract.org/2004/07/EMyService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
	<Language>UK</Language>
	<MyId>5</MyId>
	<Name>Billy</Name>
	<SomeThing></SomeThing>
	<SomethingElse></SomethingElse>
	<Timestamp></Timestamp>
</InputXMLObject>

My interface:

[ServiceContract]
public interface IEngineService
{
    [OperationContract]
    string MyServiceMethod(InputXMLObject inputXMLObject);
}

The class:

public class EngineService : IEngineService
{
	public string MyServiceMethod(InputXMLObject inputXMLObject)
	{
           // do something
	}
}