Is there a way to generate an .xsd for F# types?
We're looking at leveraging F# for our projects in the future, and would like to be able to generate .xsd schemas from F# types automatically.
Searching the web returns a lot of answers for generating types from .xsd, but not the other way around.
Does anybody know of a way to do this?
That kind of dependens on what you actually mean.
If you meant: "How do I generate XSDs from dll?" then it can be quite simply done with svcutil... Ok, given that some other conditions are met, but kind of doable:
The following command generates metadata documents for service contracts and associated types in an assembly.
svcutil myAssembly.dll
https://msdn.microsoft.com/en-us/library/aa347733(v=vs.110).aspx
xsd.exe should also kind of be able to do about the same. Im not sure about under what conditions here though, but the docs are not so "stringent" as svcutil.
The following command generates XML schemas for all types in the assembly myAssembly.dll and saves them as schema0.xsd in the current directory.
xsd myAssembly.dll
https://msdn.microsoft.com/en-us/library/x6c1kb0s(v=vs.110).aspx
If you meant "generate XSD from given *.fs file" then you're kind of out of luck (to my knowledge).
I could be wrong, but I don't see how this can be accomplished in a manner what would make it more practical than using an F# type provider based on XSD, if there is one that works sufficiently well. But then, I am not sure there is one.
Try the FSharp.Data.Xsd type provider. You can specify XSD right in the source as a string, or by referencing an XSD file external to the source. It is possible it can't create any XSD you might want.
The problem, I think, is that F# types alone isn't a practical way to specify what the XSD should look like, unless you make some compromise that perhaps you are not prepared to make.
Would you create some specific types in F# to control the mapping? I don't think having to use such types would be "leveraging F#".
Would you use code attributes or other metadata? In that case, isn't editing the XSD instead of the F# types better?
Would you simply create some rules that imply a one-to-one mapping? It could work, but might not produce the XSD and XML you want. It could become too verbose.
You would have to generate the XSD. If on the other hand you use a type provider to generate the F# types from XSD, then the generated types are instantly available. Isn't that much more practical and pleasing?
I would approach this using 'typeclasses'. Quick example (in the REPL). Suppose you have a Person
type, like type Person = { id : int64; name : string}
type Person = { id : int64; name : string}
. Then:
> ("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>"
This works by putting little converter functions for each type in the Xsd
module, and also for combinations of types, ie sum and product types. That should cover most needs. What the Xsd
module might look like:
(* 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) }
Admittedly, this is an unfamiliar technique compared to using runtime reflection. But it's also more type-safe and gives you finer-grained control over the encoding. You also probably don't need to implement all of XSD--you just need the parts that your types actually use.
链接地址: http://www.djcxy.com/p/95266.html上一篇: IWindsorContainer和IUnityContainer有什么区别?
下一篇: 有没有办法为F#类型生成.xsd?