F#记录类型的序列化

时间:2010-10-27 15:30:35

标签: serialization f# xml-serialization

我知道如何使用可变对象在F#中序列化,但有没有办法使用XmlSerializer或DataContractSerializer使用记录类型序列化/反序列化?看起来有一种方法可以使用KnownType属性为一个有区别的联合做到这一点,但我正在寻找一种方法来使用没有默认构造函数的非可变记录......

4 个答案:

答案 0 :(得分:12)

Jomo Fisher的示例code for reading data from Freebase使用DataContractJsonSerializer将数据加载到不可变的F#记录中。他使用的记录声明如下:

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

这里的关键点是DataMember属性附加到实际用于存储数据的基础字段,而不是F#编译器生成的只读属性(使用{{1} }属性上的修饰符。)

我不是100%确定这是否适用于其他类型的序列化(可能不是),但它可能是一个有用的指针,以...开头...

编辑我不确定我在这里遗漏了什么,但以下基本示例对我来说还不错:

field:

编辑2 如果您想生成更干净的XML,那么您可以添加如下属性:

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

答案 1 :(得分:12)

从F#3.0开始,现在通过将CliMutableAttribute应用于该类型来支持记录类型的序列化。例如:

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

此示例取自http://blogs.msdn.com/b/fsharpteam/archive/2012/07/19/more-about-fsharp-3.0-language-features.aspx,其中包括对此功能的讨论以及F#3.0中的其他三个新功能:三引号字符串,自动属性和未使用的变量警告。

答案 2 :(得分:9)

它不使用XmlSerializer或DataContractSerializer,但是Json.NET 6.0 includes nice F# support.

看起来像这样:

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

答案 3 :(得分:1)

您可以在类的属性上使用这一系列注释来格式化XML:

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

我在c#类中使用了属性,但是在F#中反序列化(c#类是在引用的lib中)。

在f#中:

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