我們正在考慮將來爲我們的項目充分利用F#,並希望能夠自動從F#類型生成.xsd模式。有沒有辦法爲F#類型生成.xsd?
在Web上搜索會返回很多從.xsd生成類型的答案,但不是相反。
有沒有人知道如何做到這一點?
我們正在考慮將來爲我們的項目充分利用F#,並希望能夠自動從F#類型生成.xsd模式。有沒有辦法爲F#類型生成.xsd?
在Web上搜索會返回很多從.xsd生成類型的答案,但不是相反。
有沒有人知道如何做到這一點?
這種依賴於你實際上的含義。
如果您的意思是:「我如何從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文件」,然後你那種倒黴的(據我所知)。
我可能是錯的,但我沒有看到如何以某種方式實現這一點,比使用基於XSD的F#類型提供程序更實用,如果有足夠好的工作。但是,我不確定是否有一個。
嘗試使用FSharp.Data.Xsd類型提供程序。您可以在源中將XSD指定爲字符串,或者通過引用源外部的XSD文件。它可能無法創建任何您想要的XSD。
我認爲問題在於F#類型本身並不是一種實際的方式來指定XSD應該是什麼樣子,除非您做出一些妥協,可能您沒有做好準備。
你會在F#中創建一些特定的類型來控制映射嗎?我不認爲使用這種類型將是「利用F#」。
你會使用代碼屬性或其他元數據嗎?在這種情況下,是不是更好地編輯XSD而不是F#類型?
你會簡單地創建一些暗示一對一映射的規則嗎?它可以工作,但可能不會產生你想要的XSD和XML。它可能變得太冗長。
您將不得不生成XSD。另一方面,如果您使用類型提供程序從XSD生成F#類型,則生成的類型將立即可用。這不是更實際和令人愉快嗎?
我會用'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的 全部 - 你只需要你的類型實際使用的部分。
如果您想從您的代碼中直接生成任何類型的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"
謝謝你的回答,Helge。爲了充分利用F#的簡潔性,我們確實希望自己爲F#類型生成.xsd,而不是爲底層的CLI類型生成.xsd。我們將通過.dll嘗試這種方法,以瞭解它的外觀,但仍然希望有更直接的映射可用。 –