【问题标题】:F# Add Constructor to a Record?F#将构造函数添加到记录?
【发布时间】:2010-06-09 16:27:18
【问题描述】:

基本上我想要一个结构来处理序列化为 JSON 和格式化的 xml。记录在序列化到/从 json 方面工作得很好。然而 XmlSerializer 需要一个无参数的构造器。我真的不想经历为这些构造构建类对象的练习(仅原理)。我希望有一些捷径可以将无参数构造函数添加到记录中(可能使用 wioth 语句或其他东西)。我无法让它正常运行 - 社区中是否有人运气好?

module JSONExample
    open System
    open System.IO 
    open System.Net 
    open System.Text 
    open System.Web 
    open System.Xml
    open System.Security.Authentication 
    open System.Runtime.Serialization //add assemnbly reference System.Runtime.Serialization System.Xml
    open System.Xml.Serialization
    open System.Collections.Generic 

    [<DataContract>]            
    type ChemicalElementRecord = { 
        [<XmlAttribute("name")>]
        [<field: DataMember(Name="name") >] 
        Name:string 

        [<XmlAttribute("name")>]
        [<field: DataMember(Name="boiling_point") >] 
        BoilingPoint:string 

        [<XmlAttribute("atomic-mass")>]
        [<field: DataMember(Name="atomic_mass") >] 
        AtomicMass:string 
    } 

    [<XmlRoot("freebase")>]
    [<DataContract>] 
    type FreebaseResultRecord = { 
        [<XmlAttribute("code")>]
        [<field: DataMember(Name="code") >] 
        Code:string 

        [<XmlArrayAttribute("results")>]
        [<XmlArrayItem(typeof<ChemicalElementRecord>, ElementName = "chemical-element")>]
        [<field: DataMember(Name="result") >] 
        Result: ChemicalElementRecord array

        [<XmlElement("message")>] 
        [<field: DataMember(Name="message") >] 
        Message:string 
        } 


    let getJsonFromWeb() = 
        let query = "[{'type':'/chemistry/chemical_element','name':null,'boiling_point':null,'atomic_mass':null}]"
        let query = query.Replace("'","\"") 
        let queryUrl = sprintf "http://api.freebase.com/api/service/mqlread?query=%s" "{\"query\":"+query+"}" 

        let request : HttpWebRequest = downcast WebRequest.Create(queryUrl) 
        request.Method <- "GET" 
        request.ContentType <- "application/x-www-form-urlencoded" 

        let response = request.GetResponse() 

        let result = 
            try 
                use reader = new StreamReader(response.GetResponseStream()) 
                reader.ReadToEnd(); 
            finally 
                response.Close() 

        let data = Encoding.Unicode.GetBytes(result); 
        let stream = new MemoryStream() 
        stream.Write(data, 0, data.Length); 
        stream.Position <- 0L 
        stream



    let test =
        // get some JSON from the web
        let stream = getJsonFromWeb()

        // convert the stream of JSON into an F# Record
        let JsonSerializer = Json.DataContractJsonSerializer(typeof<FreebaseResultRecord>) 
        let result: FreebaseResultRecord = downcast JsonSerializer.ReadObject(stream) 

        // save the Records to disk as JSON 
        use fs = new FileStream(@"C:\temp\freebase.json", FileMode.Create) 
        JsonSerializer.WriteObject(fs,result)
        fs.Close()

        // save the Records to disk as System Controlled XML
        let xmlSerializer = DataContractSerializer(typeof<FreebaseResultRecord>);
        use fs = new FileStream(@"C:\temp\freebase.xml", FileMode.Create) 
        xmlSerializer.WriteObject(fs,result)
        fs.Close()

        use fs = new FileStream(@"C:\temp\freebase-pretty.xml", FileMode.Create) 
        let xmlSerializer = XmlSerializer(typeof<FreebaseResultRecord>)
        xmlSerializer.Serialize(fs,result)
        fs.Close()

ignore(test)

【问题讨论】:

    标签: .net f# datacontractserializer xmlserializer


    【解决方案1】:

    Blockquote 我希望有一些捷径可以将无参数构造函数添加到记录中

    这可以在 F# 版本 3.0 中通过使用 CLIMutable 属性来实现。将此添加到无参数构造函数和读/写属性(而不是只读属性)的记录类型中。

    【讨论】:

      【解决方案2】:

      为什么不使用 DataContractSerializer(用于 Xml)而不是 XmlSerializer?这是数据合约的主要好处之一(多个投影使用相同的编程模型)。

      (无法为 F# 记录创建无参数构造函数。)

      【讨论】:

      • 你能控制 DataContractSerializer 的 XML 输出格式吗?我最初尝试过,但结果并不太好。如您所见,我有三个序列化到磁盘,第二个序列化使用该格式。
      • 我猜真正的问题是添加了命名空间和 i.Nil 属性...
      • 我明白了。快速的网络搜索表明没有;参见例如stackoverflow.com/questions/1953984/…
      • 通过将 Name 属性添加到属性中,您实际上比该帖子拥有更多的控制权。但它并不完美。真的,如果有一种将 f# 记录转换为类的廉价方法 - 这将是理想的,然后我可以对它们进行 XmlSerialize...
      • 是的,叹息,我从不使用记录,因为一旦您需要从记录中获得“更多”,从记录更改为课程有点麻烦。
      【解决方案3】:

      看起来您无法将记录更改为类 - 或向其添加 edfault 构造函数。

      提供的示例(基于本文:http://blogs.msdn.com/b/jomo_fisher/archive/2010/03/06/neat-sample-f-and-freebase.aspx)从 api.freebase.com 获取 json 流;然后我们反序列化为属性类;接下来将其作为 Json 序列化到磁盘;然后将其作为 Xml 序列化到磁盘(使用 DataContract);最后,通过对输出的最佳控制将其作为 Xml 序列化到磁盘(使用 XmlSerializer):

      注意事项:

      DataContract(family of) 属性 DataContractJsonSerializer 和 JSON.DataContractJsonSerializer - 这些发生在类名和 成员变量。数据合约 东西很简单——而且 也适用于记录类型。

      XmlSerializer(family of) 属性 在类和属性上 获取器/设置器。这需要类型 是具有默认值的对象 构造函数和属性 Getters 和 具有相关属性的设置器 他们每个人。如果属性不 有一个 getter 或一个 setter 不会序列化 - 这是一个 惊喜(我想象默认 onstructor 将确保 对象具有默认值 反序列化和设置器将 使用已序列化的内容进行更新- 但不,事实并非如此)。

      关于另一个漂亮(叹气)的事情 XmlSerialization 是那个类 不能包含在模块中。所以 我们将类型移动到命名空间...

      namespace JSONExample
          open System
          open System.IO 
          open System.Net 
          open System.Text 
          open System.Web 
          open System.Xml
          open System.Security.Authentication 
          open System.Runtime.Serialization //add assemnbly reference System.Runtime.Serialization System.Xml
          open System.Xml.Serialization
          open System.Collections.Generic 
      
          [<DataContract>]            
          type ChemicalElementRecord() =  
              [<field: DataMember(Name="name") >] 
              let mutable name: string  = ""
      
              [<field: DataMember(Name="boiling_point") >] 
              let mutable boilingPoint: string =""
      
              [<field: DataMember(Name="atomic_mass") >] 
              let mutable atomicMass: string  = ""
      
              [<XmlAttribute("name")>]
              member this.Name with get() = name and set v = name <- v
      
              [<XmlAttribute("boiling-point")>]
              member this.BoilingPoint with get()  = boilingPoint  and set v = boilingPoint <- v
      
              [<XmlAttribute("atomic-mass")>]
              member this.AtomicMass with get() = atomicMass  and set v = atomicMass <- v
      
          [<XmlRoot("freebase")>]
          [<DataContract>] 
          type FreebaseResultRecord() =  
      
              [<field: DataMember(Name="code") >] 
              let mutable code: string = ""
      
              [<field: DataMember(Name="result") >] 
              let mutable result: ChemicalElementRecord array = Array.empty
      
              [<field: DataMember(Name="message") >] 
              let mutable message: string = ""
      
              [<XmlElement("message")>] 
              member this.Message with get() : string = message and set v = message <- v
      
              [<XmlArrayAttribute("chemical-elements")>]
              [<XmlArrayItem(typeof<ChemicalElementRecord>, ElementName = "chemical-element")>]
              member this.Result with get() = result and set v = result <- v
      
              [<XmlAttribute("code")>]
              member this.Code with get() = code and set v = code <- v
      
          module Test = 
              let getJsonFromWeb() = 
                  let query = "[{'type':'/chemistry/chemical_element','name':null,'boiling_point':null,'atomic_mass':null}]"
                  let query = query.Replace("'","\"") 
                  let queryUrl = sprintf "http://api.freebase.com/api/service/mqlread?query=%s" "{\"query\":"+query+"}" 
      
                  let request : HttpWebRequest = downcast WebRequest.Create(queryUrl) 
                  request.Method <- "GET" 
                  request.ContentType <- "application/x-www-form-urlencoded" 
      
                  let response = request.GetResponse() 
      
                  let result = 
                      try 
                          use reader = new StreamReader(response.GetResponseStream()) 
                          reader.ReadToEnd(); 
                      finally 
                          response.Close() 
      
                  let data = Encoding.Unicode.GetBytes(result); 
                  let stream = new MemoryStream() 
                  stream.Write(data, 0, data.Length); 
                  stream.Position <- 0L 
                  stream
      
      
      
              let test =
                  // get some JSON from the web
                  let stream = getJsonFromWeb()
      
                  // convert the stream of JSON into an F# Record
                  let JsonSerializer = Json.DataContractJsonSerializer(typeof<FreebaseResultRecord>) 
                  let result: FreebaseResultRecord = downcast JsonSerializer.ReadObject(stream) 
      
                  // save the Records to disk as JSON 
                  use fs = new FileStream(@"C:\temp\freebase.json", FileMode.Create) 
                  JsonSerializer.WriteObject(fs,result)
                  fs.Close()
      
                  // save the Records to disk as System Controlled XML
                  let xmlSerializer = DataContractSerializer(typeof<FreebaseResultRecord>);
                  use fs = new FileStream(@"C:\temp\freebase.xml", FileMode.Create) 
                  xmlSerializer.WriteObject(fs,result)
                  fs.Close()
      
                  use fs = new FileStream(@"C:\temp\freebase-pretty.xml", FileMode.Create) 
                  let xmlSerializer = XmlSerializer(typeof<FreebaseResultRecord>)
                  xmlSerializer.Serialize(fs,result)
                  fs.Close()
      
      
              ignore(test)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-20
        • 2011-08-19
        • 1970-01-01
        相关资源
        最近更新 更多