2017-02-21 101 views
7

我們正在考慮將來爲我們的項目充分利用F#,並希望能夠自動從F#類型生成.xsd模式。有沒有辦法爲F#類型生成.xsd?

在Web上搜索會返回很多從.xsd生成類型的答案,但不是相反。

有沒有人知道如何做到這一點?

回答

4

這種依賴於你實際上的含義。

如果您的意思是:「我如何從dll生成XSD?」那麼它可以很簡單地完成與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文件」,然後你那種倒黴的(據我所知)。

+0

謝謝你的回答,Helge。爲了充分利用F#的簡潔性,我們確實希望自己爲F#類型生成.xsd,而不是爲底層的CLI類型生成.xsd。我們將通過.dll嘗試這種方法,以瞭解它的外觀,但仍然希望有更直接的映射可用。 –

0

我可能是錯的,但我沒有看到如何以某種方式實現這一點,比使用基於XSD的F#類型提供程序更實用,如果有足夠好的工作。但是,我不確定是否有一個。

嘗試使用FSharp.Data.Xsd類型提供程序。您可以在源中將XSD指定爲字符串,或者通過引用源外部的XSD文件。它可能無法創建任何您想要的XSD。

我認爲問題在於F#類型本身並不是一種實際的方式來指定XSD應該是什麼樣子,除非您做出一些妥協,可能您沒有做好準備。

  • 你會在F#中創建一些特定的類型來控制映射嗎?我不認爲使用這種類型將是「利用F#」。

  • 你會使用代碼屬性或其他元數據嗎?在這種情況下,是不是更好地編輯XSD而不是F#類型?

  • 你會簡單地創建一些暗示一對一映射的規則嗎?它可以工作,但可能不會產生你想要的XSD和XML。它可能變得太冗長。

您將不得不生成XSD。另一方面,如果您使用類型提供程序從XSD生成F#類型,則生成的類型將立即可用。這不是更實際和令人愉快嗎?

0

我會用'typeclasses'來解決這個問題。快速示例(在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的 全部 - 你只需要你的類型實際使用的部分。

0

如果您想從您的代碼中直接生成任何類型的XSD,請查看此F#腳本。它生成一個F#記錄類型的XSD。腳本使用三個.NET程序集:System.Runtime.Serialization.dll,System.Runtime.Serialization.Xml,System.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" 
相關問題