【问题标题】:FSharp.Data type providers and reflection: how do I examine the properties of an XmlProvider type?FSharp.Data 类型提供程序和反射:如何检查 XmlProvider 类型的属性?
【发布时间】:2016-10-12 11:36:57
【问题描述】:

我正在尝试使用XmlProvider 来解析一些非常大的数据文件(大约 50 MB)。由于它们太大,使用真实的数据文件作为样本是不切实际的,所以我创建了一个样本 XML 文件,我希望是可用数据的代表性样本。但是,我不能 100% 确定我已经涵盖了该 50 兆字节文件中的所有可能元素,因此我试图验证我的样本是否具有代表性。我正在尝试使用反射来帮助进行验证,但我遇到了问题。

首先,背景。我不确定的原因是因为我正在解析的 XML 文件本质上具有以下(非常扁平的)数据结构:

<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>

(在真实的数据文件中,“Guid001”等是真实的 GUID;但对于这个虚构的例子,我保持简单。)

基本上,它是一个包含大量object 元素的平面数据文件,每个元素都指向该数据来自程序中的一个C# 类实例。这些元素半随机地混合在一起,在同一个数据文件中表示了几个不同的类族。 (这就是为什么我的虚构数据文件将绘图形状与人员记录混合在一起的原因——我正在使用的真实数据文件在单个文件中具有类似的关注点。)

正如我所说,我为我的代表性样本或多或少随机选择了一堆记录。我已经尝试从每个类中至少选择一个,以便涵盖大多数属性名称,但是如果(例如)我最终为 Quux 类选择 Guid004 而不是 Guid003,该怎么办?然后我提供的类型实际上不会知道 SecondaryColor 属性。我想我可以在我提供的类型上使用.GetType(),然后调用.GetProperties() 来获取我提供的类型认为它知道的所有属性的列表。但是当我这样做时:

let firstObject = rootElement.Objects[0]
printfn "%A" firstObject.GetType().GetProperties()

正如我所期望的,我得到的只是两个属性,而不是得到一个名为ColorShapeChildrenFirstName 等的属性列表, XElement_Print

我想,我可以遍历我真实数据中的所有XElements,并将它们的一组子元素名称放在一起。然后从我的样本数据中取出一组子元素名称,并比较两组。如果这两个集合相等(例如,它们之间的差异是空集合),那么我就会知道我已经覆盖了样本数据中的所有基础。

但是,我使用 XmlProvider 正是因为我不想处理XElement 的麻烦及其怪癖(如XNames everywhere instead of strings 等)。我知道检索 XmlProvider 提供的类型的有效属性列表是可能的,因为 Atom 中的自动完成下拉列表(通过 Ionide)正是为我提供了:所有有效属性的列表。但标准的 .Net 反射方法在应用于 XmlProvider 提供的类型时似乎并没有达到我的预期。

因此,既然反射似乎没有按照我的预期进行,我应该如何获取 XmlProvider 为我的类型创建的有效属性列表?

【问题讨论】:

    标签: xml f# type-providers


    【解决方案1】:

    XML 类型提供者是一个擦除类型提供者,所有代表 XML 元素的对象在编译后的代码中都成为称为 FSharp.Data.Runtime.BaseTypes.XmlElement 的同一类型的值。提供的属性将被删除并替换为一段通过名称查找访问属性值的代码。

    这意味着反射将永远无法看到提供的属性。获得这些的唯一方法是访问底层XElement 并直接使用它。例如,要获取子元素,可以这样写:

    [ for e in firstObject.XElement.Elements() -> e.Name.LocalName ]
    

    在您的示例中的第一个元素上,这将返回一个带有 ["color"; "shape"; "children"] 的列表。

    【讨论】:

    • 谢谢。虽然显然不是我想要的答案,但这正是我需要的答案,因为现在我将停止寻找基于反射的解决方案,而直接使用 XElements。
    • 另外,感谢您对[ for item in sequence -&gt; value ] 语法的提醒。我忘记了它的存在,并且一直在使用更详细的 [ for item in sequence do yield value ] 等效项。对于该常见用例,较短的语法肯定更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-01
    • 1970-01-01
    • 2016-05-10
    • 2010-09-17
    相关资源
    最近更新 更多