有没有一种方法可以为F#类型生成.xsd文件?

7
我们希望在未来的项目中利用F#,并希望能够自动从F#类型生成.xsd模式。在网上搜索会返回很多从.xsd生成类型的答案,但没有反过来的答案。有人知道如何做到这一点吗?
4个回答

4

这有点取决于你的意思。

如果你的意思是:“我如何从dll生成XSDs?”,那么可以使用svcutil轻松完成... 好吧,前提是满足其他一些条件,但还是可行的:

以下命令为程序集中的服务契约和相关类型生成元数据文档。

svcutil myAssembly.dll

https://msdn.microsoft.com/en-us/library/aa347733(v=vs.110).aspx

xsd.exe也应该能够做类似的事情。虽然我不确定具体条件,但文档并不像svcutil那么“严格”。
下面的命令将为myAssembly.dll程序集中的所有类型生成XML模式,并将它们保存为schema0.xsd文件到当前目录中。
xsd myAssembly.dll 

https://msdn.microsoft.com/en-us/library/x6c1kb0s(v=vs.110).aspx

如果您的意思是“从给定的*.fs文件生成XSD”,那么很遗憾(据我所知)无法实现。

谢谢你的回答,Helge。我们确实希望为F#类型本身生成.xsd文件,而不是底层的CLI类型,以利用F#的简洁性。我们将尝试通过.dll的方法来看看效果如何,但仍然希望有更直接的映射可用。 - Ben Schäffner

0

我可能错了,但我不认为有比使用基于XSD的F#类型提供程序更实用的方法来完成这项任务。如果有一个足够好的,那就太棒了。但是,我不确定是否真的有这样的一个。

试试FSharp.Data.Xsd类型提供程序。您可以将XSD作为字符串直接指定在源码中,或者通过引用源码外部的XSD文件来指定它。它可能无法创建任何您想要的XSD。

我认为问题在于,仅凭F#类型本身并不是指定XSD应该如何看起来的实际方式,除非您做出一些可能您没有准备好做出的妥协。

  • 您是否会创建某些特定类型的F#以控制映射?我认为不必使用这些类型,这并不是"利用F#"的好方法。

  • 您是否会使用代码属性或其他元数据?在这种情况下,编辑XSD而不是F#类型是否更好呢?

  • 您是否只需创建一些暗示一对一映射的规则?这可能有效,但可能不会产生您想要的XSD和XML。它可能会变得过于冗长。

你需要生成XSD。另一方面,如果你使用类型提供程序从XSD生成F#类型,那么生成的类型将立即可用。这不是更实用和令人愉悦吗?


0
如果您想从代码中生成任何类型的XSD,则可以查看这个F#脚本。它可以生成F#记录类型的XSD。该脚本使用了三个.NET程序集:System.Runtime.Serialization.dllSystem.Runtime.Serialization.XmlSystem.Xml
#r "System.Runtime.Serialization.dll"
#r "System.Runtime.Serialization.Xml.dll"
#r "System.Xml.dll"

open System
open System.IO
open System.Linq
open System.Text
open System.Text.RegularExpressions
open System.Xml
open System.Runtime.Serialization

type [<DataContract>] CommitInfo = { 
  [<field: DataMember(Name="id") >]
  id: string
  [<field: DataMember(Name="date") >]
  date: DateTime 
  [<field: DataMember(Name="issueUrl") >]
  issueUrl: string
  [<field: DataMember(Name="issueId") >]
  issueId: int
  [<field: DataMember(Name="message") >]
  message: string
  [<field: DataMember(Name="url") >]
  url: string 
}

let getXmlWriter (stream: Stream) = 
    //let utf8noBOM = new UTF8Encoding(false)
    let settings = new XmlWriterSettings()
    settings.Indent <- true
    settings.Encoding <- Encoding.UTF8  
    //settings.OmitXmlDeclaration <- true
    XmlWriter.Create(stream, settings)

let streamToString (stream: Stream) = 
    stream.Position <- int64 0
    use sr = new StreamReader(stream)
    sr.ReadToEnd()

let getResultFromStream (streamWriter: Stream -> unit) = 
    use stream = new MemoryStream ()
    streamWriter stream
    streamToString stream

let exporter = XsdDataContractExporter()
exporter.Export(typeof<CommitInfo array>)
let schemas = exporter.Schemas.Schemas().Cast<Schema.XmlSchema>() |> Array.ofSeq
let schema = schemas.[1]

fun s -> s |> getXmlWriter |> schema.Write
|> getResultFromStream 
|> printfn "%s"

0
我会使用“类型类”来处理这个问题。以下是一个在REPL中的快速示例。
假设你有一个名为Person的类型,如下所示: type Person = { id : int64; name : string} 然后:
> ("id", Xsd.int64, "name", Xsd.string)
  |> Xsd.record2 "Person"
  |> Xsd.root
  |> Xsd.to_string;;
val it : string =
  "<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:complexType name="Person">
  <xsd:sequence>
    <xsd:element name="id" type="long"/>
    <xsd:element name="name" type="string"/>
  </xsd:sequence>
</xsd:complexType>
</xsd:schema>"

这是通过在Xsd模块中为每种类型以及类型组合(即和与积类型)编写小型转换器函数来实现的。这应该可以满足大多数需求。下面是Xsd模块的可能样式:
(* xsd.fsi *)

/// Just a string marked by the type of data whose XSD it holds.
/// Implementation is private so that callers can't create any XSD
/// they like.
type 'a t

/// Gives us the string representation of the XSD.
val to_string : 'a t -> string

/// Wraps an XSD encoding inside the <xsd:schema> tag pair.
val root : 'a t -> 'a t

// Primitive types.

val int : int t
val int64 : int64 t
val string : string t

/// Encode a two-field record's type (name and fields along with their
/// types) as XSD.
val record2 : string -> string * 'a1 t * string * 'a2 t -> 'a t

(* xsd.fs *)

type 'a t = { to_string : string }

let to_string xsd = xsd.to_string
let root xsd =
  { to_string =
      sprintf "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
  %s
</xsd:schema>" xsd.to_string }

let int = { to_string = "integer" }
let int64 = { to_string = "long" }
let string = { to_string = "string" }

/// Helper for record fields.
let element name typ =
  sprintf "<xsd:element name=\"%s\" type=\"%s\"/>" name typ

let record2 name (field1, xsd1, field2, xsd2) =
  { to_string =
      sprintf
        "<xsd:complexType name=\"%s\">
  <xsd:sequence>
    %s
    %s
  </xsd:sequence>
</xsd:complexType>"
        name
        (element field1 xsd1.to_string)
        (element field2 xsd2.to_string) }

诚然,与使用运行时反射相比,这是一种不太熟悉的技术。但它更加类型安全,并且可以对编码进行更细粒度的控制。您也可能不需要实现所有的XSD——您只需要使用您的类型实际使用的部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接