2010-10-27 16 views
11

So come serializzare in F # utilizzando oggetti mutabili, ma esiste un modo per serializzare/deserializzare utilizzando i tipi di record utilizzando XmlSerializer o DataContractSerializer? sembra che ci sia un modo per farlo per un'unione discriminata usando l'attributo KnownType, ma sto cercando un modo per usare record non mutabili senza costruttore predefinito ...F # Serializzazione dei tipi di record

+0

ho finito con l'utilizzo dell'analisi in xml, il che è stato altrettanto valido nel mio caso, quello che ho provato non funzionava per produrre xml pulito, anche con i suggerimenti di Tomas per usare gli attributi di System.Xml.Serialization, il mio sospetto era che fosse Non ho intenzione di lavorare, ma ho deciso di provare comunque e loro no, credo perché il namespace del datacontract ignora questi. il mio requisito era quello di utilizzare record non modificabili per questo esercizio, ma se il tuo obiettivo è solo la serializzazione xml, non è diverso dal suo utilizzo in qualsiasi altro linguaggio .net, basta creare classi, decorarle e usare XmlSerializer, funzionerà. – Alex

risposta

9

L'esempio code for reading data from Freebase di Jomo Fisher usa DataContractJsonSerializer per carica i dati in record F # immutabili. La dichiarazione del record che usa assomiglia a questo:

[<DataContract>] 
type Result<'TResult> = { // ' 
    [<field: DataMember(Name="code") >] 
    Code:string 
    [<field: DataMember(Name="result") >] 
    Result:'TResult // ' 
    [<field: DataMember(Name="message") >] 
    Message:string } 

Il punto chiave qui è che l'attributo il DataMember è attaccato al campo sottostante che viene effettivamente utilizzato per memorizzare i dati e non alla proprietà di sola lettura che il compilatore F # genera (usando il modificatore field: sull'attributo).

Io non sono sicuro al 100% se si tratta di andare a lavorare con altri tipi di serializzazione (probabilmente non), ma può essere un puntatore utile per cominciare ...

EDIT Sono non sono sicuro se mi manca qualcosa qui, ma il seguente esempio di base funziona bene per me:

module Demo 

#r "System.Runtime.Serialization.dll" 

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

type Test = 
    { Result : string[] 
    Title : string } 

do 
    let sb = new StringBuilder() 
    let value = { Result = [| "Hello"; "World" |]; Title = "Hacking" } 
    let xmlSerializer = DataContractSerializer(typeof<Test>); 
    xmlSerializer.WriteObject(new XmlTextWriter(new StringWriter(sb)), value) 
    let sr = sb.ToString() 
    printfn "%A" sr 

    let xmlSerializer = DataContractSerializer(typeof<Test>); 
    let reader = new XmlTextReader(new StringReader(sr)) 
    let obj = xmlSerializer.ReadObject(reader) :?> Test 
    printfn "Reading: %A" obj 

EDIT 2 Se si desidera generare XML più pulito allora si può aggiungere attributi in questo modo:

[<XmlRoot("test")>] 
type Test = 
    { [<XmlArrayAttribute("results")>] 
    [<XmlArrayItem(typeof<string>, ElementName = "string")>] 
    Result : string[] 
    [<XmlArrayAttribute("title")>] 
    Title : string } 
+0

questo è un inizio, ma il xml che viene generato non è "pulito", voglio dire che i tag sono come "FSI_0132.Test "invece di Test e Title_x0040_ invece del titolo. Sembra anche che non possa impostare i tag xml sul tipo con attributi come []. In altre parole, l'xml sembra intermedio, non qualcosa che potrei davvero btw, Tomas, sto iniziando a leggere il tuo libro di programmazione funzionale Real-World, sembra davvero buono finora – Alex

+0

Lo spazio dei nomi "FSI_0132" è generato da F # Interactive - nel codice compilato, sarebbe stato sostituito con il tuo spazio dei nomi effettivo Non sono sicuro del nome della proprietà, però. –

+0

@Joel: giusto, lo capisco, ma il mio obiettivo è quello di avere un XML pulito senza alcuna informazione sul namespace, come faresti se provassi a deserializzare usando la classe XmlSerializer in .net – Alex

1

È possibile utilizzare questa serie di annotazioni sulle proprietà delle classi per formattare l'XML:

[XmlRoot("root")] 
[XmlElement("some-element")] 
[XmlAttribute("some-attribute")] 
[XmlArrayAttribute("collections")] 
[XmlArrayItem(typeof(SomeClass), ElementName = "item")] 

io uso gli attributi sulle mie classi C#, ma deserializzare in F # (C# le classi sono ina riferimento lib).

in F #:

use file = new FileStream(filePath, FileMode.Open) 
let serializer= XmlSerializer(typeof<SomeClass>) 
let docs = serializer.Deserialize file :?> SomeClass 
+1

che è cool, ma il requisito è quello di utilizzare i tipi non mutabili, quindi questo approccio non funzionerà dato che la classe avrà campi mutabili – Alex

9

Non usa XmlSerializer o DataContractSerializer, ma Json.NET 6.0 includes nice F# support.

Ecco come si presenta:

type TestTarget = 
    { a: string 
     b: int } 

[<TestFixture>] 
type JsonTests() = 
    [<Test>] 
    member x.``can serialize``() = 
     let objectUnderTest = { TestTarget.a = "isa"; b = 9 } 
     let jsonResult: string = Newtonsoft.Json.JsonConvert.SerializeObject(objectUnderTest) 
     printfn "json is:\n%s" jsonResult 
     let xmlResult = Newtonsoft.Json.JsonConvert.DeserializeXmlNode(jsonResult, "root") 
     printfn "xml is:\n%s" (xmlResult.OuterXml) 

     let jsonRoundtrip = Newtonsoft.Json.JsonConvert.DeserializeObject<TestTarget>(jsonResult) 
     printfn "json roundtrip: %A" jsonRoundtrip 

     let xmlAsJson = Newtonsoft.Json.JsonConvert.SerializeXmlNode(xmlResult, Newtonsoft.Json.Formatting.Indented, true) 
     printfn "object -> json -> xml -> json:\n%A" xmlAsJson 
     let xmlRoundtrip = Newtonsoft.Json.JsonConvert.DeserializeObject<TestTarget>(xmlAsJson) 
     printfn "xml roundtrip:\n%A" xmlRoundtrip 

     Assert.That(true, Is.False) 
     () 

json is: 
{"a":"isa","b":9} 
xml is: 
<root><a>isa</a><b>9</b></root> 
json roundtrip: {a = "isa"; 
b = 9;} 
object -> json -> xml -> json: 
"{ 
    "a": "isa", 
    "b": "9" 
}" 
xml roundtrip: 
{a = "isa"; 
b = 9;} 
+0

fantastici per mostrare un modo di usare i tipi di record senza bisogno del '[]' attr (basta aggiornare il tuo nuovo nsoft) – Maslow

+0

Ho scoperto che questo non rispettava la distinzione tra maiuscole e minuscole dei nomi dei membri (venivano sempre convertiti in minuscolo). –

11

che iniziano con F # 3.0, la serializzazione di registrazione i tipi sono ora supportati applicando lo CliMutableAttribute al tipo. Esempio:

[<CLIMutable>] 
type MyRecord = { Name : string; Age : int } 

Questo esempio è tratto da http://blogs.msdn.com/b/fsharpteam/archive/2012/07/19/more-about-fsharp-3.0-language-features.aspx, che include una discussione di questa funzione e altre tre nuove funzionalità di F # 3.0: le stringhe triple-citato, proprietà automatiche, e gli avvisi di variabili non utilizzate.