Xml在不同scheme的不同文件中的公共部分的类型?

The type of the common piece of Xml in different files with different scheme?

我有以下代码来处理几个不同的 xml 文件(不同的方案),它们都有 <parameters> 的节点。现在我发现 match 部分将在很多地方使用,所以我想为它创建一个函数。

let xml1 = XmlProvider<"./file1.xml">.Parse(resp) 
match xml1.Parameters |> Seq.tryFind (fun x -> x.Name = "token")  with
| Some value -> value.Value |> Some 
| None -> None

let xml2 = XmlProvider<"./file2.xml">.Parse(resp) // different scheme but has <parameters>
match xml2.Parameters |> Seq.tryFind (fun x -> x.Name = "token")  with
| Some value -> value.Value |> Some 
| None -> None

// more very different files but has <parameters> ......

在上面的示例中,您可以看到重复了 match 部分。如何定义函数?

let getToken (parameters : <what the type?>) = 
    match parameters |> Seq.tryFind (fun x -> x.Name = "token")  with
    | Some value -> value.Value |> Some 
    | None -> None

parameters 的类型应该是什么?

更新: 我已经更新了问题。顺便说一句,这是一个显示结构类型有用性的案例吗?

这是用于测试的代码(尚无法运行)。 xml1xml2 有完全不同的 xml 方案,除了它们都有 <parameters> 部分。

let input1 = """<r1><parameters><parameter name="token">1</parameter><parameter name="other">xxx</parameter></parameters><othersOf1>..sample....</othersOf1></r1>"""
let xml1 = XmlProvider<"""<r1><parameters><parameter name="token">1</parameter><parameter name="other">xxx</parameter></parameters><othersOf1>...</othersOf1></r1>""">.Parse(input1)

let input2 = """<r2><parameters><parameter name="token">1</parameter><parameter name="other">xxx</parameter></parameters><othersOf2>...sample...</othersOf2></r2>"""
let xml2 = XmlProvider<"""<r2><parameters><parameter name="token">1</parameter><parameter name="other">xxx</parameter></parameters><othersOf2>...</othersOf2></r2>""">.Parse(input2)

let getToken (parameters: ????) =
    match parameters |> Seq.tryFind (fun x -> x.Name = "token")  with
    | Some value -> value.Value |> Some 
    | None -> None

let token1 = getToken xml1.Parameters
let token2 = getToken xml2.Parameters

一般来说,嵌套节点的类型成为XML类型提供者提供的类型的嵌套类型。

要访问类型,您首先需要使用类型别名并命名您提供的类型:

type DbToken = XmlProvider<"""<parameters>
    <parameter name="token" value="123" />
    <parameter name="token" value="123" /> 
  </parameters>""">

我正在使用内联 XML 示例,以便我可以对其进行测试,但它应该与文件相同。

文件中单个 <parameter /> 节点的类型现在是 DbToken.Parameter(名称是自动生成的唯一且可能会有所不同),因此我们现在可以编写一个函数:

let getToken (parameters:DbToken.Parameter[]) = 
    match parameters |> Seq.tryFind (fun x -> x.Name = "token")  with
    | Some value -> value.Value |> Some 
    | None -> None

下面的调用有效:

let xml = DbToken.GetSample()    
getToken xml.Parameters

我们的想法是拥有一个函数,该函数接受某种类型 ^Pseq,该类型具有 NameValue。出于教育目的,Name 可以是任何类型 'a(支持相等)并且 Value 是通用类型 'b:

let inline get name parameters =
    parameters |> Seq.tryFind (fun x -> (^P : (member Name : 'a) x) = name)
    |> Option.map (fun v -> (^P : (member Value : 'b) v))

将一些值 <parameter name="token" value="123"> 添加到 XML1 并将名称更改为 int 以及在 XML2 <parameter name="12" value="2016-12-31"> 中具有 DateTime 值允许获得:

let token1 = get "token" xml1.Parameters // : int option
let token2 = get 12 xml2.Parameters // : DateTime option

所以,是的,你是对的,这可以利用结构类型来完成。

parameters 的实际类型也取决于 nameget 的完整类型是:

name:'a -> parameters:seq< ^P> -> 'b option
  when 'a : equality and  ^P : (member get_Name :  ^P -> 'a) and
        ^P : (member get_Value :  ^P -> 'b)