Я знаю, как сериализоваться в F # с помощью изменяемых объектов, но есть ли способ сериализации/десериализации с использованием типов записей с использованием XmlSerializer или DataContractSerializer? похоже, что существует способ сделать это для дискриминационного объединения, используя атрибут KnownType, но я ищу способ использовать непеременные записи без конструктора по умолчанию...
F # Сериализация типов записей
Ответ 1
Образец код для чтения данных из 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
EDIT 2 Если вы хотите создать более чистый XML, вы можете добавить такие атрибуты:
[<XmlRoot("test")>]
type Test =
{ [<XmlArrayAttribute("results")>]
[<XmlArrayItem(typeof<string>, ElementName = "string")>]
Result : string[]
[<XmlArrayAttribute("title")>]
Title : string }
Ответ 2
Начиная с 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: тройные кавычки, автоматические свойства, и неиспользуемые переменные предупреждения.
Ответ 3
Он не использует 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;}
Ответ 4
Вы можете использовать эту серию аннотаций о свойствах классов для форматирования XML:
[XmlRoot("root")]
[XmlElement("some-element")]
[XmlAttribute("some-attribute")]
[XmlArrayAttribute("collections")]
[XmlArrayItem(typeof(SomeClass), ElementName = "item")]
Я использую атрибуты на моих классах С#, но десериализую в F # (классы С# находятся в ссылках lib).
в f #:
use file = new FileStream(filePath, FileMode.Open)
let serializer= XmlSerializer(typeof<SomeClass>)
let docs = serializer.Deserialize file :?> SomeClass