I've been looking for a component that would allow me to pass an arbitrary C# object to an XSL transform.
The naive way of doing this is to serialise the object graph using an XmlSerializer; however, if you have a large object graph, this could cause problems as far as performance is concerned. Issues such as circular references, lazy loading, proxies etc may further muddy the waters here.
A better approach is to have some kind of Adapter class that implements IXPathNavigable and XPathNavigator. One such example that I've encountered is the ObjectXPathNavigator from Byte-Force -- however, most of its key documentation is in Russian, and my initial tests seem to indicate that it has a few quirks and idiosyncrasies.
Does anyone know of either (a) any resources (overviews, tutorials, blog posts etc) about this particular in English or (b) any other alternatives that offer the same or similar functionality?
There's a (very) old MSDN article titled XPath Querying Over Objects with ObjectXPathNavigator that implements a similar class (Also called ObjectXPathNavigator, interestingly enough). I used this ages ago to query some data from Visual SourceSafe and build an RSS feed from the changelog, and it worked quite well. However, I didn't do XSLT with it, so I'm not sure if that works or not. Also, note that it was written for Framework 1.0, so you may need to update it for more recent frameoworks. Also, there may be better ways to do this now, but it would give you a starting point (and the article does a nice job of explaining how it works).
Sounds as though the problem you're trying to solve is quite interesting.
At first glance, I'd suggest writing your own implementation of an XPathNavigator
descendant - there are only 20-odd methods to write, and none of them have a particularly difficult signature.
A naive implementation using non-cached reflection would be slow(ish) but would work well as a proof of concept and you could make changes to improve performance if/when that became an issue.
However ...
... I think you may run into some difficulties that stem from your approach, not from any implementation detail.
An XML file is (by nature) a simple hierarchy of elements and attributes - there are no loops (aka cycles) in the node graph.
An XPath expression can include the operator "//
" which broadly means to search to unlimited depth. (For an exact definition, see section 2.5 of XPath 1.0.)
If you applied such an expression to an object graph with cross references (aka object cycles), then you run the risk of the XPath evaluator going into an infinite loop as it tried to recursively enumerate an effectively infinite graph.
You may be able to work around this issue by somehow keeping track of parent nodes in your XPathNavigator
and throwing an exception if a loop is detected, but I'm not sure how viable this will be.
Since the object graph may be cyclic, you cannot possibly make a Tree-based structure out of it. Your best bet is to represent the object graph by it's simplest components: nodes and vectors.
More specifically, make each node (object) an element with a unique ID (perhaps provided by C#'s GetHashCode() method?). References to other objects (vectors) would be handled by referencing the ID of the object.
Example classes (note that I don't know C# so my syntax may be a bit off):
public class SomeType {
public int myInt { get; set; }
}
public class AnotherType {
public string myString { get; set; }
public SomeType mySomeType { get; set; }
}
public class LastType {
public SomeType mySomeType { get; set; }
public AnotherType myAnotherType { get; set; }
}
public class UserTypes{
static void Main()
{
LastType lt = new LastType();
SomeType st = new SomeType();
AnotherType atype = new AnotherType();
st.myInt = 7;
atype.myString = "BOB";
atype.mySomeType = st;
lt.mySomeType = st;
lt.myAnotherType = atype;
string xmlOutput = YourAwesomeFunction(lt);
}
}
Then we would expect the value of xmlOutput to be something like this (note that the ID values chosen are completely synthetic):
<ObjectMap>
<LastType id="0">
<mySomeType idref="1" />
<myAnotherType idref="2" />
</LastType>
<SomeType id="1">
<myInt>7</myInt>
</SomeType>
<AnotherType id="2">
<myString>BOB</myString>
<mySomeType idref="1" />
</AnotherType>
</ObjectMap>
You could try something like this:
http://code.google.com/p/antix-software/wiki/AntixReflectionQuery