F# Serialization of Record types

后端 未结 4 1105
南旧
南旧 2021-02-04 08:42

I know how to serialize in F# using mutable objects, but is there a way to serialize/deserialize using record types using either XmlSerializer or the DataContractSerializer? loo

相关标签:
4条回答
  • 2021-02-04 09:08

    It doesn't use XmlSerializer or the DataContractSerializer, but Json.NET 6.0 includes nice F# support.

    It looks like this:

    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 讨论(0)
  • 2021-02-04 09:14

    You can use this series of annotations on the properties of classes to format the XML:

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

    I use the attributes on my c# classes, but deserialize in F# (c# classes are ina referenced lib).

    in f#:

    use file = new FileStream(filePath, FileMode.Open)
    let serializer= XmlSerializer(typeof<SomeClass>)
    let docs = serializer.Deserialize file :?> SomeClass
    
    0 讨论(0)
  • 2021-02-04 09:18

    Beginning with F# 3.0, serialization of record types is now supported by applying the CliMutableAttribute to the type. Example:

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

    This example is taken from http://blogs.msdn.com/b/fsharpteam/archive/2012/07/19/more-about-fsharp-3.0-language-features.aspx, which includes a discussion of this feature and three other new features in F# 3.0: triple-quoted strings, automatic properties, and unused variable warnings.

    0 讨论(0)
  • 2021-02-04 09:33

    The sample code for reading data from Freebase by Jomo Fisher uses DataContractJsonSerializer to load data into immutable F# records. The declaration of the record that he uses looks like this:

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

    The key point here is that the the DataMember attribute is attached to the underlying field that's actually used to store the data and not to the read-only property that the F# compiler generates (using the field: modifier on the attribute).

    I'm not 100% sure if this is going to work with other types of serialization (probably not), but it may be a useful pointer to start with...

    EDIT I'm not sure if I'm missing something here, but the following basic example works fine for 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 If you want to generate cleaner XML then you can add attributes like this:

    [<XmlRoot("test")>] 
    type Test = 
      { [<XmlArrayAttribute("results")>] 
        [<XmlArrayItem(typeof<string>, ElementName = "string")>] 
        Result : string[]
        [<XmlArrayAttribute("title")>] 
        Title : string }
    
    0 讨论(0)
提交回复
热议问题