F# Сериализация типов записей

Я знаю, как сериализовать в F# с использованием изменяемых объектов, но есть ли способ сериализации / десериализации с использованием типов записей с использованием XmlSerializer или DataContractSerializer? Похоже, что есть способ сделать это для различенного объединения, используя атрибут KnownType, но я ищу способ использовать неизменяемые записи без конструктора по умолчанию...

4 ответа

Решение

Пример кода для чтения данных из Freebase от Jomo Fisher использует 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# (используя field: модификатор на атрибут).

Я не уверен на 100%, если это будет работать с другими типами сериализации (вероятно, нет), но это может быть полезным указателем для начала...

РЕДАКТИРОВАТЬ Я не уверен, что я что-то здесь упускаю, но следующий базовый пример прекрасно работает для меня:

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

РЕДАКТИРОВАТЬ 2 Если вы хотите генерировать более чистый XML, то вы можете добавить атрибуты, как это:

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

Начиная с 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: строки в тройных кавычках, автоматические свойства и предупреждения о неиспользуемых переменных.

Он не использует XmlSerializer или DataContractSerializer, но Json.NET 6.0 включает в себя хорошую поддержку F#.

Это выглядит так:

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

Вы можете использовать эту серию аннотаций о свойствах классов для форматирования XML:

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

Я использую атрибуты в моих классах C#, но десериализую в F# (классы C# - это ссылочная lib).

в ф #:

use file = new FileStream(filePath, FileMode.Open)
let serializer= XmlSerializer(typeof<SomeClass>)
let docs = serializer.Deserialize file :?> SomeClass
Другие вопросы по тегам