2010-11-17 28 views
5

Tôi đang gặp sự cố khi chuyển đổi bản đồ Java/JSON thành đối tượng F # có thể sử dụng.Giải mã một bản đồ Java/JSON thành đối tượng F #

Dưới đây là trung tâm của mã của tôi:

member this.getMapFromRpcAsynchronously = 
    Rpc.getJavaJSONMap (new Action<_>(this.fillObjectWithJSONMap)) 
    () 

member this.fillObjectWithJSONMap (returnedMap : JSONMap<string, int> option) = 
    let container = Option.get(returnedMap) 
    let map = container.map 
    for thing in map do 
     this.myObject.add thing.key 
     // do stuff with thing 
    () 

Các JSON đó là trả về bởi phương pháp RPC của tôi trông như thế này:

{"id":1, "result": 
    {"map": 
     {"Momentum":12, "Corporate":3, "Catalyst":1}, 
    "javaClass":"java.util.HashMap"} 
} 

Tôi đang cố gắng để ánh xạ nó vào một F # DataContract trông như thế này:

[<DataContract>] 
type JSONMap<'T, 'S> = { 
    [<DataMember>] 
    mutable map : KeyValuePair<'T, 'S> array 
    [<DataMember>] 
    mutable javaClass : string 
} 

[<DataContract>] 
type JSONSingleResult<'T> = { 
    [<DataMember>] 
    mutable javaClass: string 
    [<DataMember>] 
    mutable result: 'T 
} 

Cuối cùng, phương pháp F # thực hiện cuộc gọi RPC thực tế (Rpc.getJa) vaJSONMap ở trên) trông giống như sau:

let getJavaJSONMap (callbackUI : Action<_>) = 
    ClientRpc.getSingleRPCResult<JSONSingleResult<JSONMap<string, int>>, JSONMap<string, int>> 
     "MyJavaRpcClass" 
     "myJavaRpcMethod" 
     "" // takes no parameters 
     callbackUI 
     (fun (x : option<JSONSingleResult<JSONMap<string, int>>>) -> 
      match x.IsSome with 
       | true -> Some(Option.get(x).result) 
       | false -> None 
     ) 

Lúc biên dịch, tôi không gặp lỗi. Phương thức RPC của tôi được gọi, và kết quả được trả về (sử dụng Fiddler để xem cuộc gọi thực tế trả lại &). Tuy nhiên, có vẻ như F # đang gặp sự cố khi kết hợp JSON vào DataContract của tôi, vì bản đồ trả về ở trên cùng luôn luôn là rỗng.

Bất kỳ suy nghĩ hoặc lời khuyên nào sẽ được đánh giá cao. Cảm ơn bạn.

Trả lời

1

Hmm đây là một vấn đề phức tạp. Tôi giả sử điều này:

{"map": 
     {"Momentum":12, "Corporate":3, "Catalyst":1}, 
    "javaClass":"java.util.HashMap"} 

có thể chứa một số lượng trường thay đổi. Và trong ký hiệu JSON chuyển thành một đối tượng (các đối tượng javascript về cơ bản (hoặc rất giống) với các bản đồ). Tôi không biết nếu điều này sẽ dịch sang F # trực tiếp.

Nó có thể được ngăn chặn không được phép bằng cách gõ F # tĩnh so với gõ động của javascript.

bạn có thể phải tự viết thói quen chuyển đổi.


Ok có được một vài lỗi nhỏ trong hợp đồng dữ liệu cho phép xác định lại JsonMap và loại bỏ các "JavaClass" thuộc tính vì nó không có trong mẫu JSON thứ cung cấp (đó là một mức độ cao hơn lên), và nó trông như thể keyvaulepair với tôi không serializing, vì vậy cho phép xác định loại của chúng ta:

type JsonKeyValuePair<'T, 'S> = { 
    [<DataMember>] 
    mutable key : 'T 
    [<DataMember>] 
    mutable value : 'S 
} 

type JSONMap<'T, 'S> = { 
    [<DataMember>] 
    mutable map : JsonKeyValuePair<'T, 'S> array 
} 

và tạo một hàm deserialize:

let internal deserializeString<'T> (json: string) : 'T = 
    let deserializer (stream : MemoryStream) = 
     let jsonSerializer 
      = Json.DataContractJsonSerializer(
       typeof<'T>) 
     let result = jsonSerializer.ReadObject(stream) 
     result 


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

    let responseObj = 
     json 
      |> convertStringToMemoryStream 
      |> deserializer 

    responseObj :?> 'T 


let run2() = 
    let json = "{\"[email protected]\":[{\"[email protected]\":\"a\",\"[email protected]\":1},{\"[email protected]\":\"b\",\"[email protected]\":2}]}" 
    let o = deserializeString<JSONMap<string, int>> json 
    () 

tôi có thể deserialize một str ing vào cấu trúc đối tượng thích hợp. Hai điều tôi muốn thấy được trả lời là

1) tại sao .NET buộc tôi phải thêm @ ký tự sau tên trường? 2) cách tốt nhất để thực hiện chuyển đổi là gì? Tôi đoán một cây cú pháp trừu tượng biểu diễn cấu trúc JSON có thể là cách để đi, và sau đó phân tích cú pháp đó thành chuỗi mới. Tôi không siêu quen thuộc với AST và phân tích cú pháp của họ mặc dù.

Có lẽ một trong các chuyên gia F # có thể trợ giúp hoặc đưa ra một bản dịch tốt hơn?


cuối cùng cộng lại trong các loại quả:

[<DataContract>] 
type Result<'T> = { 
    [<DataMember>] 
    mutable javaClass: string 
    [<DataMember>] 
    mutable result: 'T 
} 

và một chức năng bản đồ chuyển đổi (công trình trong trường hợp này - nhưng có nhiều khu vực của yếu bao gồm các định nghĩa đồ đệ quy vv):

let convertMap (json: string) = 
    let mapToken = "\"map\":" 
    let mapTokenStart = json.IndexOf(mapToken) 
    let mapTokenStart = json.IndexOf("{", mapTokenStart) 
    let mapObjectEnd = json.IndexOf("}", mapTokenStart) 
    let mapObjectStart = mapTokenStart 
    let mapJsonOuter = json.Substring(mapObjectStart, mapObjectEnd - mapObjectStart + 1) 
    let mapJsonInner = json.Substring(mapObjectStart + 1, mapObjectEnd - mapObjectStart - 1) 
    let pieces = mapJsonInner.Split(',') 
    let convertPiece state (piece: string) = 
     let keyValue = piece.Split(':') 
     let key = keyValue.[0] 
     let value = keyValue.[1] 
     let newPiece = "{\"key\":" + key + ",\"value\":" + value + "}" 
     newPiece :: state 

    let newPieces = Array.fold convertPiece [] pieces 
    let newPiecesArr = List.toArray newPieces 
    let newMap = String.Join(",", newPiecesArr) 
    let json = json.Replace(mapJsonOuter, "[" + newMap + "]") 
    json 



let json = "{\"id\":1, \"result\": {\"map\": {\"Momentum\":12, \"Corporate\":3, \"Catalyst\":1}, \"javaClass\":\"java.util.HashMap\"} } " 
printfn <| Printf.TextWriterFormat<unit>(json) 
let json2 = convertMap json 
printfn <| Printf.TextWriterFormat<unit>(json2) 
let obj = deserializeString<Result<JSONMap<string,int>>> json2 

Nó vẫn inisits trên dấu @ ở những nơi khác nhau - mà tôi không nhận được ...


cách thêm chuyển đổi w/workaround cho vấn đề dấu và

let convertMapWithAmpersandWorkAround (json: string) = 
    let mapToken = "\"map\":" 
    let mapTokenStart = json.IndexOf(mapToken) 
    let mapObjectEnd = json.IndexOf("}", mapTokenStart) 
    let mapObjectStart = json.IndexOf("{", mapTokenStart) 
    let mapJsonOuter = json.Substring(mapTokenStart , mapObjectEnd - mapTokenStart + 1) 
    let mapJsonInner = json.Substring(mapObjectStart + 1, mapObjectEnd - mapObjectStart - 1) 
    let pieces = mapJsonInner.Split(',') 
    let convertPiece state (piece: string) = 
     let keyValue = piece.Split(':') 
     let key = keyValue.[0] 
     let value = keyValue.[1] 
     let newPiece = "{\"[email protected]\":" + key + ",\"[email protected]\":" + value + "}" 
     newPiece :: state 

    let newPieces = Array.fold convertPiece [] pieces 
    let newPiecesArr = List.toArray newPieces 
    let newMap = String.Join(",", newPiecesArr) 
    let json = json.Replace(mapJsonOuter, "\"[email protected]\":[" + newMap + "]") 
    json 



let json = "{\"id\":1, \"result\": {\"map\": {\"Momentum\":12, \"Corporate\":3, \"Catalyst\":1}, \"javaClass\":\"java.util.HashMap\"} } " 
printfn <| Printf.TextWriterFormat<unit>(json) 
let json2 = convertMapWithAmpersandWorkAround json 
printfn <| Printf.TextWriterFormat<unit>(json2) 
let obj = deserialize<Result<JSONMap<string,int>>> json2 

thêm:

[<DataContract>] 

trên các hồ sơ sửa chữa các vấn đề Ký hiệu &.

+0

tôi nhận thấy rằng khi tôi lưu trữ các loại F # Ghi vào RavenDB, họ sẽ được lưu trữ với tên thuộc tính thường xuyên, và tên thuộc tính tương tự tiếp theo là một dấu hiệu @. Tôi nghi ngờ phiên bản @ là trường sao lưu cho thuộc tính công khai trong loại bản ghi. Tạo lớp của riêng tôi thay vì sử dụng một bản ghi đã loại bỏ các thuộc tính @ bổ sung - bạn có thể phải làm một cái gì đó như thế. –

2

Đây là những gì tôi nấu chín lên:

open System.Web.Script.Serialization // from System.Web.Extensions assembly 

let s = @" 
    {""id"":1, ""result"": 
     {""map"": 
      {""Momentum"":12, ""Corporate"":3, ""Catalyst"":1}, 
     ""javaClass"":""java.util.HashMap""} 
    } 
    " 

let jss = new JavaScriptSerializer() 
let o = jss.DeserializeObject(s) 

// DeserializeObject returns nested Dictionary<string,obj> objects, typed 
// as 'obj'... so add a helper dynamic-question-mark operator 
open System.Collections.Generic 
let (?) (o:obj) name : 'a = (o :?> Dictionary<string,obj>).[name] :?> 'a 

printfn "id: %d" o?id 
printfn "map: %A" (o?result?map 
        |> Seq.map (fun (KeyValue(k:string,v)) -> k,v) 
        |> Seq.toList) 
// prints: 
// id: 1 
// map: [("Momentum", 12); ("Corporate", 3); ("Catalyst", 1)] 
Các vấn đề liên quan