Archive for the ‘C#’ Category

C# string to object model using method attributes, reflection, anonymous types and LINQ.

19 May

I came across a problem the other day where I needed to create a solution that accessed an old school string based transactional interface. The problem was a relatively simple one:

  1. Convert an object model into a string in order to be sent
  2. Receive a string and build an object model

The solution had to be robust because the transactional interface was very nit picky and the strings had to be 100% formed or it will throw all sorts of errors. It also had to be extensible allowing for more transactions to be added in the future.

I’m sure there are many ways to solve this problem however I wanted to make it scalable and declarative. So, along with a few pointers from my colleague Andrew, I decided to utilise method attributes, reflection and LINQ to solve the problem.

The following code is development code to prove a concept. Use at own risk ;-)

Building a string from an object model

Firstly I had to define my data transfer class, the restrictions imposed on its properties and the  wrapper template required by the transactional interface.

In the following example my class Foo contains the property Bar which as to be a maximum of 20 characters and must conform to the Regex displayed. Foo is also to be wrapped in the input template of max size 20.

[InputTemplate("Template Bar='{Bar}' End", 20)]
public class Foo
{
    [InputStringField(20, "^[a-zA-Z0-9]+$")]
    public string Bar { get; set; }
}

Using this approach I can define as may data transfer classes that are needed by my application and declare how they are to represented to the transactional interface.

The next step was to create a string builder that will convert the a data transfer object into a string based on the restrictions defined in the attributes of its class. I did this by reflecting the attributes and applying them to the properties of the data transfer object.

private static string BuildInputString(this T obj)
{
  var attrbutes = obj.GetType()
                     .GetCustomAttributes(typeof (InputTemplateAttribute), true)
                     .Cast();
    if (attrbutes.Count() != 1)
        throw new InvalidOperationException();

    string template = attrbutes.First().Template;

    foreach (var property in
      from prop in obj.GetType().GetProperties()
      let attributes = prop.GetCustomAttributes(typeof(InputStringFieldAttribute), true)
                           .Cast()
      where attributes.Count() > 0
      select new
             {
               PropertyInfo = prop,
               Attribute = attributes.First()
             })
    {
      if (property.PropertyInfo.GetValue(obj, null) == null)
        throw new NullReferenceException()
      string value = property.PropertyInfo.GetValue(obj, null).ToString();
      string name = property.PropertyInfo.Name;
      int length = property.Attribute.Length;

      if (value.Length > length)
        throw new InvalidOperationException();

      var regex = new Regex(property.Attribute.Format);
      if (!regex.IsMatch(value))
        throw new InvalidOperationException();

      template = template.Replace("{" + name + "}", value.PadRight(length).ToUpper());
    }
    return template;
}

The method above utilises anonymous types and reflection so it can be applied to any object that’s class definition uses the attributes defined above.

The string is then constructed using a helper function.

Foo foo = new Foo { Bar = "bar " };
string someString = foo.BuildInputString();

Building an object model from a string

Now we have to go back the other way. The transaction interface returns a string that will always conform to a set standard. So the output is declared in the same way that the input class was declared. For many of my data transfer scenarios the same class was used for input and output therefore the Input and Output attributes can be added to the same class and properties.

[InputTemplate("Template Bar='{Bar}' at Date='{Bar1}' End")]
[OutputTemplate(5)]
public class Foo
{
    [InputStringField(20, "^[a-zA-Z0-9]+$")]
    [OutputStringField(5, 0)]
    public string Bar { get; set; }    

    [OutputStringField(5, 1)]
    public string Bar1 { get; set; }
}

Now, just like the string builder above, we construct our Foo object list based on the constraints imposed on the properties in its attributes. In this example, the output string will always contain a counter of length 5 and will contain two ordered properties of max 5 characters.

public static IEnumerable BuildList(this string outputString) where T : new()
{
    if (string.IsNullOrEmpty(outputString))
        throw new InvalidOperationException();

    var obj = new T();
    var classAttrs = obj.GetType()
                        .GetCustomAttributes(typeof (OutputTemplateAttribute), true)
                        .Cast();

    if (classAttrs.Count() != 1)
        throw new InvalidOperationException();

    int count = int.Parse(outputString.Substring(0, classAttrs.First().CounterLength));

    for (int i = 0; i < count; i++)
    {
        obj = new T();
        foreach (var property in
            from prop in obj.GetType().GetProperties()
                let attributes =
                    prop.GetCustomAttributes(typeof (OutputStringFieldAttribute), true)
                        .Cast()
                        .OrderBy(a => a.Ordinal)
                where attributes.Count() > 0
                select new
                        {
                            PropertyInfo = prop,
                            Attribute = attributes.First()
                        })
        {
            string value = outputString.Substring(0, property.Attribute.Length);
            outputString = outputString.Remove(0, property.Attribute.Length);
            property.PropertyInfo.SetValue(obj, value.Trim(), null);
        }
        yield return obj;
    }
}
var fooList = someString.BuildList();

Method Attributes

I don’t think there is a need to show the attributes here as they are very simple. What you see in the constructor is what is stored in the properties of the attribute.

Conclusion

The solution above is a simplified version of what I am intending to develop, however it does show the concept of what I am doing. The solution is scalable and extensible because it can add more data transfer classes for different transactions as needed.

 
Comments Off

Posted in C#