2017-02-21 10 views
7

Мы смотрим на использование F # для наших проектов в будущем и хотели бы иметь возможность автоматически генерировать схемы .xsd из типов F #.Есть ли способ генерации .xsd для типов F #?

Поиск в Интернете возвращает много ответов для генерации типов из .xsd, но не наоборот.

Кто-нибудь знает, как это сделать?

ответ

4

Это зависит от того, что вы на самом деле имеете в виду.

Если вы имели в виду: «Как мне генерировать XSD из dll?» то это можно сделать просто с помощью svcutil ... Хорошо, учитывая, что выполняются некоторые другие условия, но вид выполнимости:

Следующая команда генерирует документы метаданных для контрактов на обслуживание и связанных типов в сборке.

svcutil myAssembly.dll 

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

xsd.exe должен также вид быть в состоянии делать то же самое. Я не уверен в каких условиях здесь, но документы не настолько «строгие», как svcutil.

Следующая команда генерирует XML-схемы для всех типов в сборке myAssembly.dll и сохраняет их как schema0.xsd в текущем каталоге.

xsd myAssembly.dll 

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

Если вы имели в виду "генерировать XSD из данных * .fs файл", то ты вроде не повезло (насколько мне известно).

+0

Спасибо за ваш ответ, Хельдж. Мы действительно хотели бы генерировать сами .xsd для типов F #, а не базовые типы CLI, чтобы использовать сжатие F #. Мы попробуем подход через .dll, чтобы посмотреть, как он выглядит, но все еще надеется, что доступно более точное отображение. –

0

Возможно, я ошибаюсь, но я не понимаю, как это можно сделать так, чтобы сделать его более практичным, чем использование поставщика типа F # на основе XSD, если он работает достаточно хорошо. Но тогда я не уверен, что он есть.

Попробуйте поставщика FSharp.Data.Xsd. Вы можете указать XSD прямо в источнике в виде строки или путем ссылки на файл XSD, внешний по отношению к источнику. Возможно, он не может создать какой-либо XSD, который может вам понадобиться.

Проблема, я думаю, состоит в том, что сами типы F # не являются практичным способом определения того, как должен выглядеть XSD, если только вы не сделаете какой-то компромисс, который, возможно, вы не готовы сделать.

  • Вы бы создали определенные типы в F # для управления отображением? Я не думаю, что использовать такие типы будет «использование F #».

  • Не могли бы вы использовать атрибуты кода или другие метаданные? В этом случае лучше не редактировать XSD вместо F # типов?

  • Вы просто создали бы некоторые правила, которые подразумевают взаимное сопоставление? Он может работать, но может не создавать XSD и XML, которые вы хотите. Это может стать слишком многословным.

Вам нужно будет сгенерировать XSD. Если, с другой стороны, вы используете поставщика типов для генерации типов F # из XSD, тогда сгенерированные типы становятся доступными мгновенно.Разве это не намного практичнее и приятнее?

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) } 

Следует признать, что это незнакомый метод по сравнению с Using Runtime отражения. Но он также более безопасен по типу и дает вам более тонкий контроль над кодировкой . Вам также, вероятно, не нужно реализовывать все XSD - вам просто нужны части, которые на самом деле используют ваши типы.

0

Если вы хотите сгенерировать XSD любого типа прямо из вашего кода, то посмотрите на этот скрипт F #. Он генерирует XSD типа записи F #. Сценарий использует три сборки .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" 
Смежные вопросы