Reputation: 36708
I'm trying to use an XmlProvider to parse some very large data files (about 50 MB). Since they're so large, it's not practical to use a real data file as the sample, so I've created a sample XML file with what I hope is a representative sample of the available data. However, I'm not 100% sure that I've covered all the possible elements in that 50-megabyte file, so I'm trying to verify that my sample is representative. I'm trying to use reflection to help with that verification, but I'm running into a problem.
First, the background. The reason I'm not sure is because the XML files I'm parsing have, essentially, the following (very flat) data structure:
<root-element>
<object class="Foo" guid="Guid001">
<color>Brown</color>
<shape>Square</shape>
<children>
<childRef guid="Guid003" />
</children>
</object>
<object class="Bar" guid="Guid002">
<firstName>John</firstName>
<lastName>Smith</lastName>
</object>
<object class="Quux" guid="Guid003" parentGuid="Guid001">
<secondaryColor>Maroon</secondaryColor>
<stroke>Dashed</stroke>
<shape>Circle</shape>
</object>
<object class="Quux" guid="Guid004">
<color>Blue</color>
<stroke>Dotted</stroke>
<shape>Hexagon</shape>
</object>
</root-element>
(In the real data file, "Guid001" and so on are real GUIDs; but for this fictional example, I kept them simple.)
Basically, it's a flat data file with a lot of object
elements, each of which to a C# class instance in the program this data came from. The elements are mixed semi-randomly together, with several different class families represented in the same data file. (Which is why my fictional data file mixes drawing shapes with person records -- the real data files I'm working with have a similar mix of concerns in a single file.)
As I said, I've chosen a bunch of records more or less randomly for my representative sample. I've tried to pick at least one from every class so that I'll have most property names covered, but what if (for example) I ended up picking Guid004 for the Quux class instead of Guid003? Then my provided type would actually not know about the SecondaryColor
property. I figured I could just use .GetType()
on my provided type, and then call .GetProperties()
to get a list of all the properties that my provided type thinks it knows about. But when I do this:
let firstObject = rootElement.Objects[0]
printfn "%A" firstObject.GetType().GetProperties()
Instead of getting a list of properties named Color
, Shape
, Children
, FirstName
, etc., as I was expecting, what I get is precisely two properties, XElement
and _Print
.
I could, I suppose, iterate through all the XElement
s in my real data and put together a set of their child element names. Then take the set of child element names from my sample data, and compare the two sets. If the two sets are equal (e.g., the difference between them is the empty set), then I'll know I've covered all my bases in the sample data.
However, I'm using XmlProvider precisely because I don't want to deal with the hassle of XElement
and its quirks (like XName
s everywhere instead of strings and so on). I know it's possible to retrieve the list of valid properties for an XmlProvider-provided type, because the autocomplete dropdown in Atom (via Ionide) is giving me exactly that: a list of all the valid properties. But standard .Net reflection methods don't seem to do what I was expecting when applied to XmlProvider-provided types.
So since reflection doesn't seem to be doing what I expect, what should I be doing to get the list of valid properties that the XmlProvider created for my type?
Upvotes: 3
Views: 426
Reputation: 243106
XML type provider is an erasing type provider and all the objects that represent XML elements become values of the same type called FSharp.Data.Runtime.BaseTypes.XmlElement
in the compiled code. The provided properties are erased and are replaced with a piece of code that accesses the property value via a name lookup.
This means that reflection will never be able to see the provided properties. The only way to get those is to access the underlying XElement
and use that directly. For example, to get child elements, you can write:
[ for e in firstObject.XElement.Elements() -> e.Name.LocalName ]
On the first element from your sample, this returns a list with ["color"; "shape"; "children"]
.
Upvotes: 6