1

A webservice I use (I have no control over it) returns an XML string, which I convert to an XDcoument and then create a list of objects of a particular type:

private static List<ProductDetail> productList(XmlDocument _xDoc) {
    XDocument xdoc = XDocument.Load(new XmlNodeReader(_xDoc));
    var pList = from p in xdoc.Root.Elements("DataRow")
                select new ProductDetail
                {
                    Product  = (string)p.Element("Product"),
                    ProductDesc = (string)p.Element("ProductDesc"),
                    ExtraKey = (string)p.Element("ExtraKey"),
                    SalesGroup = (string)p.Element("SalesGroup"),
                    Donation = (string)p.Element("Donation"),
                    Subscription = (string)p.Element("Subscription"),
                    StockItem = (string)p.Element("StockItem"),
                    MinimumQuantity = (string)p.Element("MinimumQuantity"),
                    MaximumQuantity = (string)p.Element("MaximumQuantity"),
                    ProductVATCategory = (string)p.Element("ProductVATCategory"),
                    DespatchMethod = (string)p.Element("DespatchMethod"),
                    SalesDescription = (string)p.Element("SalesDescription"),
                    HistoryOnly = (string)p.Element("HistoryOnly"),
                    Warehouse = (string)p.Element("Warehouse"),
                    LastStockCount = (string)p.Element("LastStockCount"),
                    UsesProductNumbers = (string)p.Element("UsesProductNumbers"),
                    SalesQuantity = (string)p.Element("SalesQuantity"),
                    AmendedBy = (string)p.Element("AmendedBy")
                };
    return pList.ToList();
}

This works fine and is very fast. However it means I have to maintain this code separately from the model if it changes and I was just wondering if there was a shortcut to avoid me having to specify each individual field as I'm doing? I already have a class for ProductDetail so is there some way of using that at the object level? I've a feeling that the answer may be "yes, but using reflection" which will probably have a negative impact on the process speed so is not something I'd be keen on.

1 Answer 1

2
+100

There is another option that I can think of (beyond the two you already talked about in your question.. i.e. Manual mapping and Reflection based approach.

Dynamic Methods

It is DynamicMethod (The MSDN link has example as well)

This approach can give you best of both worlds.. i.e.

  • Performance
  • Dynamic

But the catch is, you trade it off with

  • Increased code complexity
  • Reduced debug ability.

It can be thought of as hybrid of the two, in the sense, it is can be as flexible / dynamic as you'd like it to be (effort will also vary accordingly), and yet hold the performance benefits similar to manually mapped objects (your sample code above).

With this approach, there is a one time cost of initializing your DynamicMethod at appropriate time (application startup / first use etc).. and then cache it for subsequent use. If you need the mapper only a handful number of times.. then it can be much less useful.. But I am assuming that is not the case for your scenario.

Technique I'd recommend

You'd notice from the example, that creating a DynamicMethod involves emitting IL op-codes at runtime (and reflection as well), and that can look very complex and difficult task, because it is more complex code and harder to debug. However, what I tend to do in this situation, is write the method I'd like to emit using DynamicMethod by hand (you already have that), and then study IL generated for that method by the compiler.

In other words, you don't have to be a master at writing IL by hand.. If you are not already familiar how to write IL for a given scenario, just write it up in plain C# as you imagine it.. compile it, and then use ILDASM to figure out how you want to emit similar IL for your DynamicMethod.

Other Options - Deserializers

For the sake of completeness, I'd say the problem you are trying to solve is in general that of deserializing XML payload into plain objects POCOs. It is not very clear from the question if you even considered them, and excluded them as an option, or they weren't even considered.

If you didn't even look in that direction, you can start with what is already available in .Net - DataContractSerializer. There can be other implementations which you may be able to find on the internet.. which may suit your needs.

The reasons why they may not be a good fit for your scenario (if I understand it right) could be -

  • Deserializers tend to be generic in functionality, and hence certainly not the same level of performance as code you have above.

May be there is a deserializer out there which uses DynamicMethod for performance, but I have never seen one. Also note that different implementations can obviously have different performance characteristics

  • The data may not lend itself for easy use with common / famous deserializers.

Like if the XML you have is deeply nested, and you want to map properties / element at different levels without creating complex object hierarchy. (One might now argue that such problems can be solved with XSL transforms.)

  • The implementation may not have the features you may really need.

Like what if the object to which data needs to be mapped is of Generic Type.

Conclusion

  • Manual mapping is fastest, but least flexible.

  • Reflection will certainly be slow, but can provide higher flexibility.

  • DynamicMethod (part of System.Reflection.Emit namespace) can give you most flexibility and performance (assuming high use with cached instance), if you are willing to pay the price of even higher complexity and development effort.

  • Deserializers give you varying degree of flexibility and performance, but are not always suitable.

EDIT: Realized, that for completeness, some more options could be added to the list.

  • T4 templates - Also hard to develop (IMHO) and debug / troubleshoot (Depends on complexity of what you are trying to achieve with them. I had to debug them by installing a VS add-in, and debug by attaching one Visual Studio instance as debugee from another Visual Studio instance as debugger - ugly. May be there are better ways, but I'd avoid them). Manual action may still be required to refresh generated code. (There is a good possibility that it can be automated during build, but I have never tried it)

  • Code generation - You write custom code generation tool, and hook it as a pre-build step in appropriate projects. (You could do this code generation as part of build, or in theory, also after your application is deployed and runs the first time, but for this scenario, build time generation should be more suitable.)

Sign up to request clarification or add additional context in comments.

4 Comments

I was beginning to despair of ever getting an answer on this, thank you! An earlier version of the code used an XMLSerializer.Deserialize loop on each returned node and it was sloooooow as there were 240-ish nodes returned. 7-10 seconds whereas my code above is a fraction of a second for the same task. There are eventually going to be up to 10 different object types returned, so I'll need to look closely at dynamic methods to see if that will help. Am I right in thinking I'd need a dynamic method for each separate XML type/model I want to deserialize?
Agreed.. As I already stated, Serializers can be very slow, which is why some developers like to show off their custom OSS implementations :) (Jokes apart, some of them are indeed better than those built in .Net). For your latter part, if you can generalize the rules for mapping, it should be possible for you to create one implementation of the mapper, and initialize it with as many dynamic methods as you like... Think how you would do it with reflection, and keep it generic.. Only that instead of using pure reflection, you'd do a hybrid of emitting dynamic methods at runtime.
Example (I just making this up - just to make the point) - MapperFactory.Initialize(List<Type> mappedTypes)) IMapper<T> MapperFactory.GetMapper() T IMapper<T>.Map(string xml). All the magic will be in MapperFactory.Initialize, which would use Reflection to go through each type, and create and cache a DynamicMethod for mapping xml to an instance of that type. This Dynamic method can then be wrapped in an Interface, and returned as the mapper for a given type, as many times as needed.
I don't know how I forgot to mention few more options as part of Other Options.. Had thought about them, but then missed them later (in fact I lost few edits in this long answer due to browser crash.. broke my chain of thought).. So sorry.. please check updated answer, just in case the other two options are also of interest to you..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.